@ Пако прав. AuthorizeAttribute
не имеет ничего общего с выбором действия. Его предложение не было правильным, поэтому благодаря ему я немного покопался в коде MVC и сам придумал наиболее подходящее решение.
Решение, как и предполагалось
В MVC есть точка расширяемости для этих вещей. По сути, вам нужно написать собственный ActionMethodSelectionAttribute
, который справится с этим. Я создал один, который выбирает действие на основе авторизации пользователя (анонимного или авторизованного). Вот код:
/// <summary>
/// Attribute restricts controller action execution only to either anonymous or authenticated users
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class AllowAuthenticatedAttribute : ActionMethodSelectorAttribute
{
/// <summary>
/// Gets or sets a value indicating whether this <see cref="AllowAuthorizedAttribute"/> allows authenticated or anonymous users to execute decorated controller action.
/// </summary>
/// <value><c>true</c> if authenticated users are allowed to execute the action; <c>false</c> if anonymous users are allowed to execute the action.</value>
public bool Authenticated { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="AllowAuthorizedAttribute"/> class.
/// </summary>
/// <param name="authenticated">If set to <c>true</c> only authorized users will be able to access this action.</param>
public AllowAuthenticatedAttribute(bool authenticated)
{
this.Authenticated = authenticated;
}
/// <summary>
/// Determines whether the action method selection is valid for the specified controller context.
/// </summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="methodInfo">Information about the action method.</param>
/// <returns>
/// true if the action method selection is valid for the specified controller context; otherwise, false.
/// </returns>
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
return this.Authenticated == controllerContext.HttpContext.User.Identity.IsAuthenticated;
}
}
Дополнительное наблюдение
Когда я украсил свои методы действий своим пользовательским атрибутом, я все еще получал то же исключение, пока не добавил [HttpGet]
к своим действиям GET. Это почему? Я нашел ответ в блок-схеме в Pro ASP.NET MVC Framework книга ( проверить его самостоятельно ). Было сгенерировано исключение, потому что было более одного метода действия с ActionMethodSelectorAttribute
. Обычно мы просто украшаем POST-действия, но в этом случае все они были оформлены. 2 для анонимных и 2 для аутентифицированных пользователей. Вот почему вы должны использовать оба HttpGet
и HttpPost
для методов действия при добавлении к ним дополнительных атрибутов селектора.
Действия моего контроллера теперь выглядят так
[HttpGet]
[AllowAuthenticated(false)]
[ActionName("Same-name")]
public ActionResult AnonAction() { ... }
[HttpPost]
[AllowAuthenticated(false)]
[ActionName("Same-name")]
public ActionResult AnonAction(ModelData data) { ... }
[HttpGet]
[Authorize]
[AllowAuthenticated(true)]
[ActionName("Same-name")]
public ActionResult AuthAction() { ... }
[HttpPost]
[Authorize]
[AllowAuthenticated(true)]
[ActionName("Same-name")]
public ActionResult AuthAction(OtherData data) { ... }