Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET MVC Routing - bind 2 routeMap parameters

I need to parse an url like the following

/controller/action/subaction/id

At the moment i'm using a switch on subaction to see what actually needs to be done. EG:

    public ActionResult Members(string subaction, long id=0)
    {
        switch (subaction)
        {
            case "Details":
                var member = _payment.GetMember(id);
                return View("Members_details", member);
            default:
                var members = _payment.GetMembers().ToList();
                return View("Members_list", members);
        }
    }

This works, but i'd rather have seperate actions for each event, directly accessed from the route. If possible I would like to combine the action and subaction in the routemap to access the right action.

  • /controller/action/ would call action()
  • /controller/action/subaction would call action_subaction()
  • /controller/action/subaction/id would call action_subaction(id)

Is that possible directly from the routemap?

like image 936
Hugo Delsing Avatar asked Nov 05 '22 18:11

Hugo Delsing


1 Answers

Custom action method selector class

If I were you I'd write an action method selector and use that to avoid branching in your actions. I've written one that separates actions that take optional parameters (thus avoiding branched code within action - simplified unit testing). Default Asp.net MVC defined route has an optional id parameter

{controller}/{action}/{id}
id = UrlParameter.Optional

So it makes sense to have these two action methods:

public ActionResult Index() { ... }
public ActionResult Index(int id) { ... }

I've accomplished just that by writing a custom action selector filter. Here's a detailed blog post that describes the whole thing and provides some code you can peek at.

How about a solution for your problem

In your case this means you'd have to write a custom action method selector class called SubactionAttribute and then simply decorate your actions with it:

[Subaction("Details")]
public ActionResult Members(long id)
{
    var member = _payment.GetMember(id);
    return View("Members.Details", member);
}

[Subaction] // no name would mean default subaction (when not provided)
public ActionResult Members()
{
    var members = _payment.GetMembers().ToList();
    return View("Members.List", members);
}

I'm not going to write the whole class for you, but I will only point you in the right direction so you can follow the same path to get where you're headed:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class SubactionAttribute : ActionMethodSelectorAttribute
{
    #region Properties

    /// <summary>
    /// Gets subaction name.
    /// </summary>
    public string Name { get; private set; }

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="SubactionAttribute"/> class.
    /// </summary>
    public SubactionAttribute()
        : this(null)
    {
        // does nothing
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="SubactionAttribute"/> class.
    /// </summary>
    /// <param name="subactionName">Sub-action name</param>
    public SubactionAttribute(string subactionName)
    {
        this.Name = subactionName;
    }

    #endregion

    #region ActionMethodSelectorAttribute implementation

    /// <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, MethodInfo methodInfo)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        // get the value of subaction here
        string subName = /* this part you'll have to write */

        // determine whether subaction matches
        return this.Name == subName;
    }

    #endregion
}
like image 135
Robert Koritnik Avatar answered Nov 15 '22 11:11

Robert Koritnik