* Modify DefaultControllerTypeProvider to look at the object graph to

determine if any ancestor has the "Controller" suffix.

* Introduce NonControllerAttribute to opt out of Controller detection.

Fixes #1274
This commit is contained in:
Pranav K 2015-02-11 22:54:38 -08:00
parent 8a9d0d16f7
commit 5e69d90076
31 changed files with 833 additions and 95 deletions

32
Mvc.sln
View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22606.0
VisualStudioVersion = 14.0.22604.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
EndProject
@ -140,6 +140,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RazorCompilerCacheWebSite",
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.PageExecutionInstrumentation", "src\Microsoft.AspNet.PageExecutionInstrumentation\Microsoft.AspNet.PageExecutionInstrumentation.kproj", "{4DA2D7C1-A7B6-4C01-B57D-89E6EA4609DE}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "UserClassLibrary", "test\WebSites\UserClassLibrary\UserClassLibrary.kproj", "{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ControllerDiscoveryConventionsWebSite", "test\WebSites\ControllerDiscoveryConventionsWebSite\ControllerDiscoveryConventionsWebSite.kproj", "{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -810,6 +814,30 @@ Global
{4DA2D7C1-A7B6-4C01-B57D-89E6EA4609DE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4DA2D7C1-A7B6-4C01-B57D-89E6EA4609DE}.Release|x86.ActiveCfg = Release|Any CPU
{4DA2D7C1-A7B6-4C01-B57D-89E6EA4609DE}.Release|x86.Build.0 = Release|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Debug|x86.ActiveCfg = Debug|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Debug|x86.Build.0 = Debug|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Release|Any CPU.Build.0 = Release|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Release|x86.ActiveCfg = Release|Any CPU
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C}.Release|x86.Build.0 = Release|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Debug|x86.ActiveCfg = Debug|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Debug|x86.Build.0 = Debug|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|Any CPU.Build.0 = Release|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|x86.ActiveCfg = Release|Any CPU
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -878,5 +906,7 @@ Global
{551DC89E-2A13-4CF2-83D7-1ADD802443D5} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{42C5D417-4060-48F4-BB28-E9E179007779} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{4DA2D7C1-A7B6-4C01-B57D-89E6EA4609DE} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{C651F432-4EBE-41A6-BAD2-3E07CCBA209C} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{A19022EF-9BA3-4349-94E4-F48E13E1C8AE} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal

View File

@ -4,7 +4,7 @@
using System;
using System.IO;
using System.Linq.Expressions;
using System.Security.Principal;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
@ -12,16 +12,21 @@ using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
public class Controller : IActionFilter, IAsyncActionFilter, IDisposable
/// <summary>
/// Base class for an MVC controller.
/// </summary>
public abstract class Controller : IActionFilter, IAsyncActionFilter, IDisposable
{
private DynamicViewData _viewBag;
private ViewDataDictionary _viewData;
private ActionContext _actionContext;
/// <summary>
/// Gets the request-specific <see cref="IServiceProvider"/>.
/// </summary>
public IServiceProvider Resolver
{
get
@ -30,6 +35,9 @@ namespace Microsoft.AspNet.Mvc
}
}
/// <summary>
/// Gets the <see cref="HttpContext"/> for the executing action.
/// </summary>
public HttpContext Context
{
get
@ -38,6 +46,9 @@ namespace Microsoft.AspNet.Mvc
}
}
/// <summary>
/// Gets the <see cref="HttpRequest"/> for the executing action.
/// </summary>
public HttpRequest Request
{
get
@ -46,6 +57,9 @@ namespace Microsoft.AspNet.Mvc
}
}
/// <summary>
/// Gets the <see cref="HttpResponse"/> for the executing action.
/// </summary>
public HttpResponse Response
{
get
@ -54,14 +68,20 @@ namespace Microsoft.AspNet.Mvc
}
}
/// <summary>
/// Gets the <see cref="AspNet.Routing.RouteData"/> for the executing action.
/// </summary>
public RouteData RouteData
{
get
{
return ActionContext?.RouteData;
}
}
}
/// <summary>
/// Gets the <see cref="ModelStateDictionary"/> that contains the state of the model and of model-binding validation.
/// </summary>
public ModelStateDictionary ModelState
{
get
@ -97,16 +117,28 @@ namespace Microsoft.AspNet.Mvc
}
}
/// <summary>
/// Gets or sets the <see cref="ActionBindingContext"/>.
/// </summary>
[Activate]
public ActionBindingContext BindingContext { get; set; }
/// <summary>
/// Gets or sets the <see cref="IModelMetadataProvider"/>.
/// </summary>
[FromServices]
public IModelMetadataProvider MetadataProvider { get; set; }
/// <summary>
/// Gets or sets the <see cref="IUrlHelper"/>.
/// </summary>
[FromServices]
public IUrlHelper Url { get; set; }
public IPrincipal User
/// <summary>
/// Gets or sets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
/// </summary>
public ClaimsPrincipal User
{
get
{
@ -138,7 +170,7 @@ namespace Microsoft.AspNet.Mvc
return _viewData;
}
set
{
{
if (value == null)
{
throw
@ -149,6 +181,9 @@ namespace Microsoft.AspNet.Mvc
}
}
/// <summary>
/// Gets the dynamic view bag.
/// </summary>
public dynamic ViewBag
{
get
@ -1111,12 +1146,18 @@ namespace Microsoft.AspNet.Mvc
return ModelState.IsValid;
}
/// <inheritdoc />
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases all resources currently used by this <see cref="Controller"/> instance.
/// </summary>
/// <param name="disposing"><c>true</c> if this method is being invoked by the <see cref="Dispose"/> method,
/// otherwise <c>false</c>.</param>
protected virtual void Dispose(bool disposing)
{
}

View File

@ -12,10 +12,21 @@ namespace Microsoft.AspNet.Mvc
{
public class DefaultAssemblyProvider : IAssemblyProvider
{
private readonly ILibraryManager _libraryManager;
public DefaultAssemblyProvider(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Gets the set of assembly names that are used as root for discovery of
/// MVC controllers, view components and views.
/// </summary>
// DefaultControllerTypeProvider uses CandidateAssemblies to determine if the base type of a POCO controller
// lives in an assembly that references MVC. CandidateAssemblies excludes all assemblies from the
// ReferenceAssemblies set. Consequently adding WebApiCompatShim to this set would cause the ApiController to
/// fail this test.
protected virtual HashSet<string> ReferenceAssemblies { get; } = new HashSet<string>(StringComparer.Ordinal)
{
"Microsoft.AspNet.Mvc",
@ -23,16 +34,8 @@ namespace Microsoft.AspNet.Mvc
"Microsoft.AspNet.Mvc.ModelBinding",
"Microsoft.AspNet.Mvc.Razor",
"Microsoft.AspNet.Mvc.Razor.Host",
"Microsoft.AspNet.Mvc.Rendering",
};
private readonly ILibraryManager _libraryManager;
public DefaultAssemblyProvider(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
/// <inheritdoc />
public IEnumerable<Assembly> CandidateAssemblies
{

View File

@ -16,6 +16,9 @@ namespace Microsoft.AspNet.Mvc
/// </summary>
public class DefaultControllerTypeProvider : IControllerTypeProvider
{
private const string ControllerTypeName = nameof(Controller);
private static readonly TypeInfo ControllerTypeInfo = typeof(Controller).GetTypeInfo();
private static readonly TypeInfo ObjectTypeInfo = typeof(object).GetTypeInfo();
private readonly IAssemblyProvider _assemblyProvider;
private readonly ILogger _logger;
@ -37,17 +40,17 @@ namespace Microsoft.AspNet.Mvc
{
get
{
var assemblies = _assemblyProvider.CandidateAssemblies;
var candidateAssemblies = new HashSet<Assembly>(_assemblyProvider.CandidateAssemblies);
if (_logger.IsEnabled(LogLevel.Verbose))
{
foreach (var assembly in assemblies)
foreach (var assembly in candidateAssemblies)
{
_logger.WriteVerbose(new AssemblyValues(assembly));
}
}
var types = assemblies.SelectMany(a => a.DefinedTypes);
return types.Where(IsController);
var types = candidateAssemblies.SelectMany(a => a.DefinedTypes);
return types.Where(typeInfo => IsController(typeInfo, candidateAssemblies));
}
}
@ -55,8 +58,10 @@ namespace Microsoft.AspNet.Mvc
/// Returns <c>true</c> if the <paramref name="typeInfo"/> is a controller. Otherwise <c>false</c>.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <param name="candidateAssemblies">The set of candidate assemblies.</param>
/// <returns><c>true</c> if the <paramref name="typeInfo"/> is a controller. Otherwise <c>false</c>.</returns>
protected internal virtual bool IsController([NotNull] TypeInfo typeInfo)
protected internal virtual bool IsController([NotNull] TypeInfo typeInfo,
[NotNull] ISet<Assembly> candidateAssemblies)
{
if (!typeInfo.IsClass)
{
@ -76,17 +81,47 @@ namespace Microsoft.AspNet.Mvc
{
return false;
}
if (typeInfo.Name.Equals("Controller", StringComparison.OrdinalIgnoreCase))
if (!typeInfo.Name.EndsWith(ControllerTypeName, StringComparison.OrdinalIgnoreCase) &&
!DerivesFromController(typeInfo, candidateAssemblies))
{
return false;
}
if (!typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
!typeof(Controller).GetTypeInfo().IsAssignableFrom(typeInfo))
if (typeInfo.IsDefined(typeof(NonControllerAttribute)))
{
return false;
}
return true;
}
private bool DerivesFromController(TypeInfo typeInfo, ISet<Assembly> candidateAssemblies)
{
// A type is a controller if it derives from a type that is either named "Controller" or has the suffix
// "Controller". We'll optimize the most common case of types deriving from the Mvc Controller type and
// walk up the object graph if that's not the case.
if (ControllerTypeInfo.IsAssignableFrom(typeInfo))
{
return true;
}
while (typeInfo != ObjectTypeInfo)
{
var baseTypeInfo = typeInfo.BaseType.GetTypeInfo();
// A base type will be treated as a controller if
// a) it ends in the term "Controller" and
// b) it's assembly is one of the candidate assemblies we're considering. This ensures that the assembly
// the base type is declared in references Mvc.
if (baseTypeInfo.Name.EndsWith(ControllerTypeName, StringComparison.Ordinal) &&
candidateAssemblies.Contains(baseTypeInfo.Assembly))
{
return true;
}
typeInfo = baseTypeInfo;
}
return false;
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Indicates that the type and any derived types that this attribute is applied to
/// is not considered a controller by the default controller discovery mechanism.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class NonControllerAttribute : Attribute
{
}
}

View File

@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Mvc.Test
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var controller = new Controller();
var controller = new TestableController();
var originalViewData = controller.ViewData = new ViewDataDictionary<object>(metadataProvider);
var replacementViewData = new ViewDataDictionary<object>(metadataProvider);
@ -61,7 +61,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Redirect_WithParameterUrl_SetsRedirectResultSameUrl()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var url = "/test/url";
// Act
@ -77,7 +77,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void RedirectPermanent_WithParameterUrl_SetsRedirectResultPermanentAndSameUrl()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var url = "/test/url";
// Act
@ -95,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Redirect_WithParameter_NullOrEmptyUrl_Throws(string url)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(
@ -108,7 +108,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void RedirectPermanent_WithParameter_NullOrEmptyUrl_Throws(string url)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(
@ -119,7 +119,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void RedirectToAction_WithParameterActionName_SetsResultActionName()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultTemporary = controller.RedirectToAction("SampleAction");
@ -134,7 +134,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void RedirectToActionPermanent_WithParameterActionName_SetsResultActionNameAndPermanent()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultPermanent = controller.RedirectToActionPermanent("SampleAction");
@ -152,7 +152,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void RedirectToAction_WithParameterActionAndControllerName_SetsEqualNames(string controllerName)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultTemporary = controller.RedirectToAction("SampleAction", controllerName);
@ -172,7 +172,7 @@ namespace Microsoft.AspNet.Mvc.Test
string controllerName)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultPermanent = controller.RedirectToActionPermanent("SampleAction", controllerName);
@ -191,7 +191,7 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<KeyValuePair<string, object>> expected)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultTemporary = controller.RedirectToAction("SampleAction", "SampleController", routeValues);
@ -211,7 +211,7 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<KeyValuePair<string, object>> expected)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultPermanent = controller.RedirectToActionPermanent(
@ -234,7 +234,7 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<KeyValuePair<string, object>> expected)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultTemporary = controller.RedirectToAction(actionName: null, routeValues: routeValues);
@ -253,7 +253,7 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<KeyValuePair<string, object>> expected)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultPermanent = controller.RedirectToActionPermanent(null, routeValues);
@ -272,7 +272,7 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<KeyValuePair<string, object>> expected)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultTemporary = controller.RedirectToRoute(routeValues);
@ -290,7 +290,7 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<KeyValuePair<string, object>> expected)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var resultPermanent = controller.RedirectToRoutePermanent(routeValues);
@ -305,7 +305,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void RedirectToRoute_WithParameterRouteName_SetsResultSameRouteName()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var routeName = "CustomRouteName";
// Act
@ -321,7 +321,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void RedirectToRoutePermanent_WithParameterRouteName_SetsResultSameRouteNameAndPermanent()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var routeName = "CustomRouteName";
// Act
@ -340,7 +340,7 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<KeyValuePair<string, object>> expected)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var routeName = "CustomRouteName";
// Act
@ -360,7 +360,7 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<KeyValuePair<string, object>> expected)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var routeName = "CustomRouteName";
// Act
@ -377,7 +377,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Created_WithStringParameter_SetsCreatedLocation()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var uri = "http://test/url";
// Act
@ -393,7 +393,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Created_WithAbsoluteUriParameter_SetsCreatedLocation()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var uri = new Uri("http://test/url");
// Act
@ -409,7 +409,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Created_WithRelativeUriParameter_SetsCreatedLocation()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var uri = new Uri("/test/url", UriKind.Relative);
// Act
@ -425,7 +425,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void CreatedAtAction_WithParameterActionName_SetsResultActionName()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var result = controller.CreatedAtAction("SampleAction", null);
@ -444,7 +444,7 @@ namespace Microsoft.AspNet.Mvc.Test
string controllerName)
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var result = controller.CreatedAtAction("SampleAction", controllerName, null, null);
@ -460,7 +460,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void CreatedAtAction_WithActionControllerRouteValues_SetsSameValues()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var expected = new Dictionary<string, object>
{
{ "test", "case" },
@ -485,7 +485,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void CreatedAtRoute_WithParameterRouteName_SetsResultSameRouteName()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var routeName = "SampleRoute";
// Act
@ -500,7 +500,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void CreatedAtRoute_WithParameterRouteValues_SetsResultSameRouteValues()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var expected = new Dictionary<string, object>
{
{ "test", "case" },
@ -520,7 +520,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void CreatedAtRoute_WithParameterRouteNameAndValues_SetsResultSameProperties()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var routeName = "SampleRoute";
var expected = new Dictionary<string, object>
{
@ -542,7 +542,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void File_WithContents()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var fileContents = new byte[0];
// Act
@ -559,7 +559,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void File_WithContentsAndFileDownloadName()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var fileContents = new byte[0];
// Act
@ -576,7 +576,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void File_WithPath()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var path = Path.GetFullPath("somepath");
// Act
@ -593,7 +593,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void File_WithPathAndFileDownloadName()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var path = Path.GetFullPath("somepath");
// Act
@ -610,7 +610,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void File_WithStream()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var fileStream = Stream.Null;
// Act
@ -627,7 +627,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void File_WithStreamAndFileDownloadName()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var fileStream = Stream.Null;
// Act
@ -645,7 +645,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void HttpNotFound_SetsStatusCode()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var result = controller.HttpNotFound();
@ -659,7 +659,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void BadRequest_SetsStatusCode()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var result = controller.HttpBadRequest();
@ -673,7 +673,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void BadRequest_SetsStatusCodeAndValue_Object()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var obj = new object();
// Act
@ -689,7 +689,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void BadRequest_SetsStatusCodeAndValue_ModelState()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var result = controller.HttpBadRequest(new ModelStateDictionary());
@ -713,7 +713,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Controller_View_WithoutParameter_SetsResultNullViewNameAndNullViewDataModel()
{
// Arrange
var controller = new Controller()
var controller = new TestableController()
{
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
};
@ -732,7 +732,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Controller_View_WithParameterViewName_SetsResultViewNameAndNullViewDataModel()
{
// Arrange
var controller = new Controller()
var controller = new TestableController()
{
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
};
@ -751,7 +751,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Controller_View_WithParameterViewModel_SetsResultNullViewNameAndViewDataModel()
{
// Arrange
var controller = new Controller()
var controller = new TestableController()
{
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
};
@ -771,7 +771,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Controller_View_WithParameterViewNameAndViewModel_SetsResultViewNameAndViewDataModel()
{
// Arrange
var controller = new Controller()
var controller = new TestableController()
{
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
};
@ -791,7 +791,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Controller_Content_WithParameterContentString_SetsResultContent()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var actualContentResult = controller.Content("TestContent");
@ -807,7 +807,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Controller_Content_WithParameterContentStringAndContentType_SetsResultContentAndContentType()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var actualContentResult = controller.Content("TestContent", "text/plain");
@ -823,7 +823,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Controller_Content_WithParameterContentAndTypeAndEncoding_SetsResultContentAndTypeAndEncoding()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
// Act
var actualContentResult = controller.Content("TestContent", "text/plain", Encoding.UTF8);
@ -839,7 +839,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void Controller_Json_WithParameterValue_SetsResultData()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var data = new object();
// Act
@ -1144,7 +1144,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void ControllerExposes_RequestServices()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var serviceProvider = Mock.Of<IServiceProvider>();
var httpContext = new Mock<HttpContext>();
@ -1166,7 +1166,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void ControllerExposes_Request()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var request = Mock.Of<HttpRequest>();
var httpContext = new Mock<HttpContext>();
@ -1188,7 +1188,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void ControllerExposes_Response()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var response = Mock.Of<HttpResponse>();
var httpContext = new Mock<HttpContext>();
@ -1210,7 +1210,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void ControllerExposes_RouteData()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var routeData = Mock.Of<RouteData>();
@ -1325,7 +1325,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void TryValidateModelEmptyBindingContextThrowsException()
{
// Arrange
var controller = new Controller();
var controller = new TestableController();
var model = new TryValidateModelModel();
// Act & Assert
@ -1346,7 +1346,7 @@ namespace Microsoft.AspNet.Mvc.Test
ValueProvider = provider,
};
return new Controller()
return new TestableController()
{
ActionContext = actionContext,
BindingContext = bindingContext,
@ -1397,5 +1397,10 @@ namespace Microsoft.AspNet.Mvc.Test
DisposeCalled = true;
}
}
private class TestableController : Controller
{
}
}
}

View File

@ -490,8 +490,8 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var actionContext = new ActionContext();
var controller1 = new Controller();
var controller2 = new Controller();
var controller1 = new TestabilityController();
var controller2 = new TestabilityController();
// Act
controller2.ActionContext = actionContext;

View File

@ -1,7 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNet.Mvc.DefaultControllerTypeProviderControllers;
using Xunit;
@ -10,6 +11,11 @@ namespace Microsoft.AspNet.Mvc
{
public class DefaultControllerTypeProviderTest
{
private static readonly ISet<Assembly> CandidateAssemblies = new HashSet<Assembly>
{
typeof(StoreController).GetTypeInfo().Assembly
};
[Fact]
public void IsController_UserDefinedClass()
{
@ -18,7 +24,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.True(isController);
@ -32,7 +38,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.False(isController);
@ -46,7 +52,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.False(isController);
@ -60,7 +66,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.False(isController);
@ -74,7 +80,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.False(isController);
@ -88,7 +94,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.True(isController);
@ -102,7 +108,21 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_WithoutSuffixOrAncestorWithController()
{
// Arrange
var typeInfo = typeof(NoSuffixPoco).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.False(isController);
@ -116,7 +136,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.True(isController);
@ -130,7 +150,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.True(isController);
@ -144,7 +164,7 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.True(isController);
@ -158,12 +178,47 @@ namespace Microsoft.AspNet.Mvc
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
var isController = provider.IsController(typeInfo, CandidateAssemblies);
// Assert
Assert.True(isController);
}
[Theory]
[InlineData(typeof(DescendantLevel1))]
[InlineData(typeof(DescendantLevel2))]
public void IsController_ReturnsTrue_IfAncestorTypeNameHasControllerSuffix(Type type)
{
// Arrange
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(type.GetTypeInfo(), CandidateAssemblies);
// Assert
Assert.True(isController);
}
[Theory]
[InlineData(typeof(BaseNonControllerController))]
[InlineData(typeof(BaseNonControllerControllerChild))]
[InlineData(typeof(BasePocoNonControllerController))]
[InlineData(typeof(BasePocoNonControllerControllerChild))]
[InlineData(typeof(NonController))]
[InlineData(typeof(NonControllerChild))]
[InlineData(typeof(PersonModel))] // Verifies that POCO type hierarchies that don't derive from controller return false.
public void IsController_ReturnsFalse_IfTypeOrAncestorHasNonControllerAttribute(Type type)
{
// Arrange
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(type.GetTypeInfo(), CandidateAssemblies);
// Assert
Assert.False(isController);
}
private static DefaultControllerTypeProvider GetControllerTypeProvider()
{
var assemblyProvider = new FixedSetAssemblyProvider();
@ -191,7 +246,7 @@ namespace Microsoft.AspNet.Mvc.DefaultControllerTypeProviderControllers
{
}
public class Controller
public abstract class Controller
{
}
@ -211,7 +266,86 @@ namespace Microsoft.AspNet.Mvc.DefaultControllerTypeProviderControllers
{
}
public class NoSuffixPoco
{
}
public class PocoController
{
}
public class CustomBaseController
{
}
public abstract class CustomAbstractBaseController
{
}
public class DescendantLevel1 : CustomBaseController
{
}
public class DescendantLevel2 : DescendantLevel1
{
}
public class AbstractChildWithoutSuffix : CustomAbstractBaseController
{
}
[NonController]
public class BasePocoNonControllerController
{
}
public class BasePocoNonControllerControllerChild : BasePocoNonControllerController
{
}
[NonController]
public class BaseNonControllerController : Controller
{
}
public class BaseNonControllerControllerChild : BaseNonControllerController
{
}
[NonController]
public class NonControllerChild : Controller
{
}
[NonController]
public class NonController : Controller
{
}
public class DataModelBase
{
}
public class EntityDataModel : DataModelBase
{
}
public class PersonModel : EntityDataModel
{
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
@ -18,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class BasicTests
{
private readonly IServiceProvider _provider = TestHelper.CreateServices("BasicWebSite");
private readonly IServiceProvider _provider = TestHelper.CreateServices(nameof(BasicWebSite));
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
// Some tests require comparing the actual response body against an expected response baseline
@ -275,5 +276,33 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var responseData = await response.Content.ReadAsStringAsync();
Assert.Equal("This is a basic website.", responseData);
}
[Fact]
public async Task TypesWithoutControllerSuffix_DerivingFromTypesWithControllerSuffix_CanBeAccessed()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = new HttpClient(server.CreateHandler(), false);
// Act
var response = await client.GetStringAsync("http://localhost/appointments");
// Assert
Assert.Equal("2 appointments available.", response);
}
[Fact]
public async Task TypesMarkedAsNonAction_AreInaccessible()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = new HttpClient(server.CreateHandler(), false);
// Act
var response = await client.GetAsync("http://localhost/SqlData/TruncateAllDbRecords");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}
}

View File

@ -0,0 +1,143 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using ControllerDiscoveryConventionsWebSite;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.TestHost;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Runtime;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class ControllerDiscoveryConventionTests
{
private readonly IServiceProvider _provider = TestHelper.CreateServices(
nameof(ControllerDiscoveryConventionsWebSite));
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
[Fact]
public async Task AbstractControllers_AreSkipped()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost/");
// Act
var response = await client.GetAsync("Abstract/GetValue");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task TypesDerivingFromControllerBaseTypesThatDoNotReferenceMvc_AreSkipped()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost/");
// Act
var response = await client.GetAsync("SqlTransactionManager/GetValue");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task TypesMarkedWithNonController_AreSkipped()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost/");
// Act
var response = await client.GetAsync("NonController/GetValue");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task PocoTypesWithControllerSuffix_AreDiscovered()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost/");
// Act
var response = await client.GetAsync("Poco/GetValue");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("PocoController", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task TypesDerivingFromTypesWithControllerSuffix_AreDiscovered()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost/");
// Act
var response = await client.GetAsync("ChildOfAbstract/GetValue");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("AbstractController", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task TypesDerivingFromApiController_AreDiscovered()
{
// Arrange
// TestHelper.CreateServices replaces the DefaultAssemblyProvider with a provider that
// limits the set of candidate assemblies to the executing application. For this test,
// we'll switch it back to using a filtered default assembly provider.
var services = HostingServices.Create(configuration: null);
services.AddTransient<IAssemblyProvider, FilteredDefaultAssemblyProvider>();
var serviceProvider = TestHelper.CreateServices(nameof(ControllerDiscoveryConventionsWebSite), services);
var server = TestServer.Create(serviceProvider, _app);
var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost/");
// Act
var response = await client.GetAsync("PersonApi/GetValue");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("PersonApi", await response.Content.ReadAsStringAsync());
}
private class FilteredDefaultAssemblyProvider : DefaultAssemblyProvider
{
public FilteredDefaultAssemblyProvider(ILibraryManager libraryManager)
: base(libraryManager)
{
}
protected override IEnumerable<ILibraryInformation> GetCandidateLibraries()
{
var libraries = base.GetCandidateLibraries();
// Filter out other WebSite projects
return libraries.Where(library => !library.Name.Contains("WebSite") ||
library.Name.Equals(nameof(ControllerDiscoveryConventionsWebSite), StringComparison.Ordinal));
}
}
}
}

View File

@ -49,6 +49,21 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(expected, response);
}
[Fact]
public async Task TypesDerivingFromControllerPrefixedTypesAreRegistered()
{
// Arrange
var expected = "4";
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetStringAsync("http://localhost/inventory/");
// Assert
Assert.Equal(expected, response);
}
[Fact]
public async Task TypesWithControllerSuffixAreRegistered()
{
@ -84,9 +99,10 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
}
[Theory]
[InlineData("generic")]
[InlineData("nested")]
[InlineData("not-in-services")]
[InlineData("not-discovered/generic")]
[InlineData("not-discovered/nested")]
[InlineData("not-discovered/not-in-services")]
[InlineData("ClientUIStub/GetClientContent/5")]
public async Task AddControllersFromServices_UsesControllerDiscoveryContentions(string action)
{
// Arrange
@ -94,7 +110,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var client = server.CreateClient();
// Act
var response = await client.GetAsync("http://localhost/not-discovered/" + action);
var response = await client.GetAsync("http://localhost/" + action);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

View File

@ -17,6 +17,7 @@
"BasicWebSite": "1.0.0",
"CompositeViewEngineWebSite": "1.0.0",
"ConnegWebSite": "1.0.0",
"ControllerDiscoveryConventionsWebSite": "1.0.0",
"ControllersFromServicesWebSite": "1.0.0",
"CustomRouteWebSite": "1.0.0",
"ErrorPageMiddlewareWebSite": "1.0.0",

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace BasicWebSite.Controllers
{
public abstract class ApplicationBaseController
{
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace BasicWebSite.Controllers
{
[Route("/appointments")]
public class Appointments : ApplicationBaseController
{
[HttpGet("")]
public IActionResult Get()
{
return new ContentResult
{
Content = "2 appointments available."
};
}
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace BasicWebSite
{
[NonController]
public class SqlDataController
{
public int TruncateAllDbRecords()
{
// Return no. of tables truncated
return 7;
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace ControllerDiscoveryConventionsWebSite
{
public abstract class AbstractController
{
public string GetValue()
{
return nameof(AbstractController);
}
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace ControllerDiscoveryConventionsWebSite
{
public class ChildOfAbstract : AbstractController
{
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a19022ef-9ba3-4349-94e4-f48e13e1c8ae</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>51163</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ControllerDiscoveryConventionsWebSite
{
[NonController]
public class NonControllerController : Controller
{
public string GetValue()
{
return nameof(NonControllerController);
}
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Web.Http;
namespace ControllerDiscoveryConventionsWebSite
{
public class PersonApi : ApiController
{
public string GetValue()
{
return nameof(PersonApi);
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace ControllerDiscoveryConventionsWebSite
{
public class PocoController
{
public string GetValue()
{
return nameof(PocoController);
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace UserClassLibrary
{
public class SqlTransactionManager : TransactionController
{
public string GetValue()
{
return nameof(SqlTransactionManager);
}
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
namespace ControllerDiscoveryConventionsWebSite
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var configuration = app.GetTestConfiguration();
app.UseServices(services =>
{
services.AddMvc(configuration);
});
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller}/{action}/{id?}");
});
}
}
}

View File

@ -0,0 +1,21 @@
{
"commands": {
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
},
"dependencies": {
"UserClassLibrary": "1.0.0",
"Kestrel": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-*",
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
},
"frameworks": {
"aspnet50": {},
"aspnetcore50": { }
},
"webroot": "wwwroot"
}

View File

@ -0,0 +1 @@
Functional test site for verifying controllers registration rules.

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ControllersFromServicesClassLibrary
{
[NonController]
public class ClientUIStubController
{
public object GetClientContent(int id)
{
return new object();
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ControllersFromServicesClassLibrary
{
public class Inventory : ResourcesController
{
[HttpGet]
public int Get()
{
return 4;
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ControllersFromServicesClassLibrary
{
[Route("/[controller]")]
public class ResourcesController
{
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace UserClassLibrary
{
// A type with the suffix Controller that lives in an assembly that does not reference Mvc.
// This will not be treated as a controller in an Mvc application.
public class TransactionController
{
public int UpdateTransaction()
{
return 1;
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>c651f432-4ebe-41a6-bad2-3e07ccba209c</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,10 @@
{
"frameworks": {
"aspnet50": { },
"aspnetcore50": {
"dependencies": {
"System.Runtime": "4.0.20-beta-*"
}
}
}
}