Adding ActionName Attribute

This commit is contained in:
harshgMSFT 2014-04-07 16:47:56 -07:00
parent 1da2d6d9dc
commit 53b76380eb
3 changed files with 119 additions and 17 deletions

View File

@ -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; }
}
}

View File

@ -104,38 +104,43 @@ namespace Microsoft.AspNet.Mvc
private bool HasCustomAttributes(MethodInfo methodInfo)
{
var actionAttributes = GetActionCustomAttributes(methodInfo);
return actionAttributes.HttpMethodProviderAttributes.Any();
return actionAttributes.Any();
}
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()
{
HttpMethodProviderAttributes = httpMethodConstraints
HttpMethodProviderAttributes = httpMethodConstraints,
ActionNameAttribute = actionNameAttribute
};
}
private IEnumerable<ActionInfo> GetActionsForMethodsWithCustomAttributes(MethodInfo methodInfo)
{
var httpMethodConstraints = GetActionCustomAttributes(methodInfo).HttpMethodProviderAttributes;
if (!httpMethodConstraints.Any())
var actionAttributes = GetActionCustomAttributes(methodInfo);
if (!actionAttributes.Any())
{
// If the action is not decorated with any of the attributes,
// it would be handled by convention.
yield break;
}
var httpMethods = httpMethodConstraints.SelectMany(x => x.HttpMethods).Distinct().ToArray();
if (httpMethods.Any())
var actionNameAttribute = actionAttributes.ActionNameAttribute;
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
// an explicit NoAction attribute is exposed as a method with action name.
yield return new ActionInfo()
{
HttpMethods = httpMethods,
ActionName = methodInfo.Name,
RequireActionNameMatch = true
};
}
HttpMethods = httpMethods,
ActionName = actionName,
RequireActionNameMatch = true
};
}
private IEnumerable<ActionInfo> GetActionsForMethodsWithoutCustomAttributes(MethodInfo methodInfo, TypeInfo controllerTypeInfo)
@ -201,7 +206,13 @@ namespace Microsoft.AspNet.Mvc
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();
}
}
}
}

View File

@ -108,6 +108,64 @@ namespace Microsoft.AspNet.Mvc.Core.Test
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)
{
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
{
[HttpGet]