سؤال

I'm building a NopCommerce plugin and want to register a custom ActionFilter with a FilterProvider on an ActionMethod that has shared ActionName with other methods.

Here are two of the ActionMethods with same ActionName

[HttpPost, ActionName("Edit")]
[FormValueRequired("cancelorder")]      
public ActionResult CancelOrder(int id)
{...}

[HttpPost, ActionName("Edit")]
[FormValueRequired("captureorder")]      
public ActionResult CaptureOrder(int id)
{...}

In my filter provider I somehow need to separate the CancelOrder method from CaptureOrder:

public class OrderFilterProvider : IFilterProvider
{
    private readonly IActionFilter _actionFilter;

    public OrderFilterProvider(IActionFilter actionFilter)
    {
        _actionFilter = actionFilter;
    }

    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {       
        if (actionDescriptor.ControllerDescriptor.ControllerType == typeof(OrderController) &&
            actionDescriptor.ActionName.Equals("Edit"))
        {
            return new Filter[] 
            { 
                new Filter(_actionFilter, FilterScope.Action, null)
            };
        }

        return new Filter[] { };
    }
}

With this code the filter gets registered on both CancelOrder and CaptureOrder, how can I register it only on the CancelOrder method?

My idea was to separate them based on method name or the parameter value in the FormValueRequired attribute but no luck in finding a way of doing that.

FormValueRequired doesnt have any public properties and looks like:

public class FormValueRequiredAttribute : ActionMethodSelectorAttribute {
private readonly string[] _submitButtonNames;
private readonly FormValueRequirement _requirement;

public FormValueRequiredAttribute(params string[] submitButtonNames):
    this(FormValueRequirement.Equal, submitButtonNames)
{
}

public FormValueRequiredAttribute(FormValueRequirement requirement, params string[] submitButtonNames)
{
    //at least one submit button should be found
    this._submitButtonNames = submitButtonNames;
    this._requirement = requirement;
}
}

And here is what it looks like in my debugger: http://www.tiikoni.com/tis/view/?id=4843369

Is there no way to get the name of the method?

هل كانت مفيدة؟

المحلول

The only difference I can see between those 2 actions is the presence of some custom FormValueRequired attribute which unfortunately you haven't shown nor explain what it does, but I guess it is some action selector. Anyway, you could retrieve this attribute:

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
    if (actionDescriptor.ControllerDescriptor.ControllerType == typeof(OrderController) &&
        actionDescriptor.ActionName.Equals("Edit"))
    {
        // Get the FormValueRequiredAttribute that was used to decorate the action
        var formValueRequiredAttribute = actionDescriptor
            .GetCustomAttributes(typeof(FormValueRequiredAttribute), false)
            .Cast<FormValueRequiredAttribute>()
            .FirstOrDefault();

        // if the action was decorated with this attribute we could read the
        // property that was passed to the constructor (hopefully it is exposed
        // as a public property)
        if (formValueRequiredAttribute != null && 
            formValueRequiredAttribute.SomeParameter == "captureorder")
        {
            // The CaptureOrder action was invoked
            return new Filter[] 
            { 
                new Filter(_actionFilter, FilterScope.Action, null)
            };
        }
    }

    return new Filter[] { };
}

نصائح أخرى

The FormValueRequired attribute actually checks the 'name' of the posted data. So if your POSTed form has a name-value-pair with name 'cancelorder', the Action method will be executed. (Interestingly enough, FormValueRequired checks on the 'name' of the name-value-pair', but not the 'value' of the name-value-pair').

In other words, keep your ActionFilter, and let both the Action fire the ActionFilter. But in your actual ActionFilter method, check if the POSTed form has the appropriate name, with the code:

controllerContext.HttpContext.Request.Form["cancelorder"];

You can easily get the ControllerContext in your OnXXX ActionFilter method parameter. :)

One important thing to notice: even though the FormValueRequired attribute only checks the 'name' of the posted data, you will still need to provide a value for it to work.

For example, this will not work:

<input type="submit" name="continueshopping" value="" class="continue-shopping-button" />

I wanted a button with no text value, so I had to enter a blank space for the value, like this:

<input type="submit" name="continueshopping" value=" " class="continue-shopping-button" />
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top