3

I'm having trouble adding a URL parameter to every URL generated, or redirected to in an ASP MVC 4 application.

I want to generate an ID, and use this ID at any point throughout the application. Storing the id in session is not an option as a single session may have multiple browser windows/tabs open concurrently (each with a different id)


RouteConfig

routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{customId}",
            defaults: new { controller = "Home", action = "Index", customid = UrlParameter.Optional }
        );

HomeController.cs

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var customId = Guid.NewGuid();

        ControllerContext.RequestContext.RouteData.Values.Add("customId", customId);

        //How do I get this redirect to add customid to the url?
        //E.g. /Home/Start/{customId}
        return RedirectToAction("Start");
        //I could do this: But I want it this to happen for every URL, 
        //and I don't want to replicate this code everywhere
        //return RedirectToAction("Start", new { customId = customId });
    }

    public ActionResult Start()
    {
        object customId;

        //Redirect Loop
        if (!Request.RequestContext.RouteData.Values.TryGetValue("customId", out customId))
        {
            //To generate the ID
            return RedirectToAction("Index");
        }

        ViewData["customId"] = Guid.Parse(customId.ToString());

        return View();
    }

    public ActionResult Next()
    {
        object customId;

        //Redirect Loop
        if (!Request.RequestContext.RouteData.Values.TryGetValue("customId", out customId))
        {
            //To generate the ID
            return RedirectToAction("Index");
        }

        ViewData["customId"] = Guid.Parse(customId.ToString());

        return View();
    }
}

Not only do I want the ID to be automatically inserted into any Redirect results, but when a View is rendered @Url.Action() and @Html.ActionLink() should also add the ID to the generated URL's.

Start.cshtml

@*Both of these should generate an href="~/Home/Next/{customId}"*@
@Html.ActionLink("Go to Next", "Next", "Home")
<a href="@Url.Action("Next", "Home")">Go to Next</a>

How do I automatically add an ID to ALL outgoing routes in ASP MVC?

Xenolightning
  • 4,140
  • 1
  • 26
  • 34

2 Answers2

4

Create an action filter that will add the ID to the route data in the OnActionExecuting method? You can access the controller through the filter context (and the viewbag). As long as your viewbag contains the customId, you should be able to add it to the route data. At least this way you only need to remember to add the attribute on the controller.

OR

Create a base class that inherits from System.Web.Mvc.Controller and implement your own RedirectToAction. Then have all your controllers inherit form MyControllerBase. Something like this.

public class MyControllerBase : Controller
{
    public RedirectToRouteResult RedirectToAction<TController>(Expression<Func<TController, object>> actionExpression)
    {
        var custId = ControllerContext.Controller.ViewBag["customId"];
        string controllerName = typeof(TController).GetControllerName();
        string actionName = actionExpression.GetActionName();

        return RedirectToAction(actionName, controllerName, new {cId = custId});
    }
}

PART 2: Another way I've modified a URL (I knew I had the code somewhere!) on every view, I needed the URL to link from a mobile site to a full browser site and read the mappings from the database. So in my footer, I have the following:

<a id="fullSiteLink" href="<%= ViewData[AppConstants.MainSiteUrl] %>">Visit our Full Browser site</a><br />

I then added a filter to the base controller class and onactionexecuting (before the action),

public void OnActionExecuting(ActionExecutingContext filterContext)
{
    var mainSiteUrl = _mobileToMainRedirect.GetMainSiteUrl(filterContext.HttpContext.Request.Url);
    filterContext.Controller.ViewData.Add(AppConstants.MainSiteUrl, string.IsNullOrEmpty(mainSiteUrl) ? UrlHelperExtensions.FullBrowserSite(filterContext.HttpContext.Request.Url) : mainSiteUrl);
}
garethb
  • 3,951
  • 6
  • 32
  • 52
  • Both are good options. I ended up going with a global filter to add the ID in `OnActionExecuted` (after the action had run, and before the result is executed). This solves the `RedirectToAction` issue. There's the other part requiring outgoing URL's to be updated. I ended up achieving this by inserting the `customId` into `RouteData.Values` in `MvcHandler.BeginProcessRequest`, to get this working you ALSO need the `customId` in the routes too. I will post my code and accept this :) – Xenolightning Mar 19 '14 at 21:00
  • If you could post your code, that would be great! I've added the way I've done it in the past, but would be good to see another solution! – garethb Mar 19 '14 at 23:35
0

Complete shot in the dark....

You can set up the route so that if a value is not provided, you create the Id. This way, if the value is there, it will use the provided one. Otherwise, it will create one.

Since this is leveraging the routes, you will be able to generate the Id even when using: @Html.ActionLink("Go to Next", "Next", "Home")

routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{customid}",
            defaults: new { controller = "Home", action = "Index", customid = Guid.NewGuid() }
        );

NOTE: You would replace Guid.NewGuid() with your own Id generator.

Andy T
  • 10,223
  • 5
  • 53
  • 95
  • Routes are only created once. Setting the default value here would change it for the entire application. (If it was possible) it would have to be a delegate e.g. `customId = () => Guid.NewGuid();` – Xenolightning Mar 18 '14 at 23:26