Влияние MVC ModelBinder с атрибутами - PullRequest
1 голос
/ 31 марта 2012

В MVC3 (.Net) можно установить атрибут привязки для параметра типа в сигнатуре метода для метода Controller:

[HttpPost]
public ActionResult Edit([Bind(Exclude = "Name")]User user)
{
   ...
}

Я написал несколько пользовательских ModelBinder.Было бы неплохо иметь возможность влиять на их поведение, основываясь на атрибутах, установленных для параметра Type, например:

[HttpPost]
public ActionResult Edit([CustomModelBinderSettings(DoCustomThing = "True")]User user)
{
   ...
}

Однако я не могу найти способ восстановить данные атрибута.Возможно ли это?


Редактировать

Я пытаюсь получить доступ к AttributeData из пользовательского ModelBinder.В приведенном ниже примере «настройки» всегда равны нулю

public class TestBinder : DefaultModelBinder {
        public override object BindModel(
            ControllerContext controllerContext, 
            ModelBindingContext bindingContext) {

            //Try and get attribute from ModelType
            var settings = (CustomModelBinderSettingsAttribute) 
                TypeDescriptor.GetAttributes(bindingContext.ModelType)[typeof(CustomModelBinderSettingsAttribute)];

            ...

Спасибо за любую помощь.

Ответы [ 4 ]

0 голосов
/ 26 ноября 2016

Вы можете получить атрибуты как действия, так и его параметров, "перехватывая" Controller.ActionInvoker.FindAction() и сохраняя атрибуты в HttpContext.Current.Items, как указано здесь, или в расширенном ControllerContext.RequestContext, следующим образом:

public class MyControllerActionInvoker : ControllerActionInvoker
{
    protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
    {
        var action = base.FindAction(controllerContext, controllerDescriptor, actionName);

        if (action != null)
        {
            var requestContext = ExtendedRequestContext.Bind(controllerContext);

            // if necessary, use action.GetCustomAttributes() to add action's attributes

            foreach (var parameter in action.GetParameters())
            {
                var attr = parameter.GetCustomAttributes(typeof(MyBinderAttribute), false).FirstOrDefault();
                if (attr != null)
                    requestContext.Attributes.Add(parameter.ParameterName, (MyBinderAttribute)attr);
            }
        }

        return action;
    }
}

public class ExtendedRequestContext : RequestContext
{
    public Dictionary<string, MyBinderAttribute> Attributes { get; private set; }

    public static ExtendedRequestContext Bind(ControllerContext controllerContext)
    {
        var requestContext = new ExtendedRequestContext
        {
            HttpContext = controllerContext.RequestContext.HttpContext,
            RouteData = controllerContext.RequestContext.RouteData,
            Attributes = new Dictionary<string, MyBinderAttribute>()
        };

        controllerContext.RequestContext = requestContext;
        return requestContext;
    }
}

Вызывающий действие по умолчанию заменен либо в конструкторе вашего контроллера, либо на фабрике пользовательских контроллеров:

public MyController() : base()
{
    ActionInvoker = new MyControllerActionInvoker();
}

Кстати, Controller.TempData уже содержит элемент типа ReflectedParameterDescriptor, который дает вам доступ к ActionDescriptor, поэтому приведенный выше код может быть избыточным. Однако будьте осторожны, это зависит от реализации, поэтому может со временем меняться.

Наконец, получите атрибуты из этого хранилища в вашем классе подшивки:

var requestContext = (ExtendedRequestContext)controllerContext.RequestContext;

if (requestContext.Attributes.ContainsKey(bindingContext.ModelName))
{
    var attr = requestContext.Attributes[bindingContext.ModelName];
    // apply your logic here
}
0 голосов
/ 01 апреля 2012

Ответ: невозможно: (

Я прошелся по исходному коду MVC и в итоге обнаружил, что класс ControllorActionInvoker явно обращается к атрибуту bind из параметров метода действия и устанавливает их для свойства bindingContext. Без переопределения или переписывания больших частей инфраструктуры MVC невозможно получить доступ к атрибутам, добавленным к параметрам действия, из ModelBinder.

Однако, можно получить атрибуты, установленные для ViewModel , используя код, показанный в посте LukLeds:

var attr = ("GetTypeDescriptor(controllerContext, bindingContext).GetAttributes()[typeof(BindAttribute)];

Увы, это не то, что я намеревался сделать в этом случае, но это придется сделать сейчас.

0 голосов
/ 10 октября 2014

Возможно, если у вас есть (или вы создаете новый) AuthorizeAttribute (атрибут filter), который будет хранить filterContext.ActionDescriptor (ваше действие) в HttpContext.Current.Items и позже в ModelBinder, это значение будет получено. Имея ActionDescriptor, вы можете найти нужный параметр с помощью bindingContext.ModelName и проверить наличие атрибута.

На что обратить внимание:

  • ModelBinder не привязан к выполненному действию
  • используя ControllerContext, вы можете вызывать ActionName, но не вызывать реальный метод (может иметь несколько перегрузок с вашим параметром)
  • AuthorizeFilter вызывается до ModelBinder, обычный ActionFilter вызывается после ModelBinder
0 голосов
/ 31 марта 2012

Вам не нужно делать ничего больше, чем вы, чтобы получить доступ к значению свойства DoCustomThing из вашего CustomModelBinderAttribute.Где вы пытаетесь прочитать значение DoCustomThing, которое недоступно?

public class CustomModelBinderSettingsAttribute : CustomModelBinderAttribute
{
    public string DoCustomThing { get; set; }
    public override IModelBinder GetBinder()
    {
        // Pass the value of DoCustomThing to the custom model binder instance.
        MyCustomizableModelBinder binder = new MyCustomizableModelBinder(this.DoCustomThing);
        return binder;
    }
}
...