Adding ActionName Attribute
This commit is contained in:
parent
1da2d6d9dc
commit
53b76380eb
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||||
|
public sealed class ActionNameAttribute : Attribute
|
||||||
|
{
|
||||||
|
public ActionNameAttribute(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -104,38 +104,43 @@ namespace Microsoft.AspNet.Mvc
|
||||||
private bool HasCustomAttributes(MethodInfo methodInfo)
|
private bool HasCustomAttributes(MethodInfo methodInfo)
|
||||||
{
|
{
|
||||||
var actionAttributes = GetActionCustomAttributes(methodInfo);
|
var actionAttributes = GetActionCustomAttributes(methodInfo);
|
||||||
return actionAttributes.HttpMethodProviderAttributes.Any();
|
return actionAttributes.Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActionAttributes GetActionCustomAttributes(MethodInfo methodInfo)
|
private ActionAttributes GetActionCustomAttributes(MethodInfo methodInfo)
|
||||||
{
|
{
|
||||||
var httpMethodConstraints = methodInfo.GetCustomAttributes().OfType<IActionHttpMethodProvider>();
|
var attributes = methodInfo.GetCustomAttributes();
|
||||||
|
var actionNameAttribute = attributes.OfType<ActionNameAttribute>().FirstOrDefault();
|
||||||
|
var httpMethodConstraints = attributes.OfType<IActionHttpMethodProvider>();
|
||||||
return new ActionAttributes()
|
return new ActionAttributes()
|
||||||
{
|
{
|
||||||
HttpMethodProviderAttributes = httpMethodConstraints
|
HttpMethodProviderAttributes = httpMethodConstraints,
|
||||||
|
ActionNameAttribute = actionNameAttribute
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<ActionInfo> GetActionsForMethodsWithCustomAttributes(MethodInfo methodInfo)
|
private IEnumerable<ActionInfo> GetActionsForMethodsWithCustomAttributes(MethodInfo methodInfo)
|
||||||
{
|
{
|
||||||
var httpMethodConstraints = GetActionCustomAttributes(methodInfo).HttpMethodProviderAttributes;
|
var actionAttributes = GetActionCustomAttributes(methodInfo);
|
||||||
if (!httpMethodConstraints.Any())
|
if (!actionAttributes.Any())
|
||||||
{
|
{
|
||||||
|
// If the action is not decorated with any of the attributes,
|
||||||
|
// it would be handled by convention.
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var httpMethods = httpMethodConstraints.SelectMany(x => x.HttpMethods).Distinct().ToArray();
|
var actionNameAttribute = actionAttributes.ActionNameAttribute;
|
||||||
if (httpMethods.Any())
|
var actionName = actionNameAttribute != null ? actionNameAttribute.Name : methodInfo.Name;
|
||||||
|
|
||||||
|
var httpMethodProviders = actionAttributes.HttpMethodProviderAttributes;
|
||||||
|
var httpMethods = httpMethodProviders.SelectMany(x => x.HttpMethods).Distinct().ToArray();
|
||||||
|
|
||||||
|
yield return new ActionInfo()
|
||||||
{
|
{
|
||||||
// Any method which does not follow convention and does not have
|
HttpMethods = httpMethods,
|
||||||
// an explicit NoAction attribute is exposed as a method with action name.
|
ActionName = actionName,
|
||||||
yield return new ActionInfo()
|
RequireActionNameMatch = true
|
||||||
{
|
};
|
||||||
HttpMethods = httpMethods,
|
|
||||||
ActionName = methodInfo.Name,
|
|
||||||
RequireActionNameMatch = true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<ActionInfo> GetActionsForMethodsWithoutCustomAttributes(MethodInfo methodInfo, TypeInfo controllerTypeInfo)
|
private IEnumerable<ActionInfo> GetActionsForMethodsWithoutCustomAttributes(MethodInfo methodInfo, TypeInfo controllerTypeInfo)
|
||||||
|
|
@ -201,7 +206,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
private class ActionAttributes
|
private class ActionAttributes
|
||||||
{
|
{
|
||||||
public IEnumerable<IActionHttpMethodProvider> HttpMethodProviderAttributes { get; set; }
|
public IEnumerable<IActionHttpMethodProvider> HttpMethodProviderAttributes { get; set; }
|
||||||
|
public ActionNameAttribute ActionNameAttribute { get; set; }
|
||||||
|
|
||||||
|
public bool Any()
|
||||||
|
{
|
||||||
|
return ActionNameAttribute != null || HttpMethodProviderAttributes.Any();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,6 +108,64 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
Assert.Equal("Index", result.Name);
|
Assert.Equal("Index", result.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("PUT")]
|
||||||
|
[InlineData("POST")]
|
||||||
|
[InlineData("DELETE")]
|
||||||
|
[InlineData("PATCH")]
|
||||||
|
public async Task ActionNameAttribute_ActionGetsExposedViaActionName_UnreachableByConvention(string verb)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestContext = new RequestContext(
|
||||||
|
GetHttpContext(verb),
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "controller", "ActionName" },
|
||||||
|
{ "action", "RPCMethodWithHttpGet" }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await InvokeActionSelector(requestContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(null, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET", "CustomActionName_Verb")]
|
||||||
|
[InlineData("PUT", "CustomActionName_Verb")]
|
||||||
|
[InlineData("POST", "CustomActionName_Verb")]
|
||||||
|
[InlineData("DELETE", "CustomActionName_Verb")]
|
||||||
|
[InlineData("PATCH", "CustomActionName_Verb")]
|
||||||
|
[InlineData("GET", "CustomActionName_DefaultMethod")]
|
||||||
|
[InlineData("PUT", "CustomActionName_DefaultMethod")]
|
||||||
|
[InlineData("POST", "CustomActionName_DefaultMethod")]
|
||||||
|
[InlineData("DELETE", "CustomActionName_DefaultMethod")]
|
||||||
|
[InlineData("PATCH", "CustomActionName_DefaultMethod")]
|
||||||
|
[InlineData("GET", "CustomActionName_RpcMethod")]
|
||||||
|
[InlineData("PUT", "CustomActionName_RpcMethod")]
|
||||||
|
[InlineData("POST", "CustomActionName_RpcMethod")]
|
||||||
|
[InlineData("DELETE", "CustomActionName_RpcMethod")]
|
||||||
|
[InlineData("PATCH", "CustomActionName_RpcMethod")]
|
||||||
|
public async Task ActionNameAttribute_DifferentActionName_UsesActionNameFromActionNameAttribute(string verb, string actionName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestContext = new RequestContext(
|
||||||
|
GetHttpContext(verb),
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "controller", "ActionName" },
|
||||||
|
{ "action", actionName }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await InvokeActionSelector(requestContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(actionName, result.Name);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<ActionDescriptor> InvokeActionSelector(RequestContext context)
|
private async Task<ActionDescriptor> InvokeActionSelector(RequestContext context)
|
||||||
{
|
{
|
||||||
return await InvokeActionSelector(context, _actionDiscoveryConventions);
|
return await InvokeActionSelector(context, _actionDiscoveryConventions);
|
||||||
|
|
@ -178,6 +236,24 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ActionNameController
|
||||||
|
{
|
||||||
|
[ActionName("CustomActionName_Verb")]
|
||||||
|
public void Put()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[ActionName("CustomActionName_DefaultMethod")]
|
||||||
|
public void Index()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[ActionName("CustomActionName_RpcMethod")]
|
||||||
|
public void RPCMethodWithHttpGet()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class HttpMethodAttributeTests_RestOnlyController
|
private class HttpMethodAttributeTests_RestOnlyController
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue