Reputation: 17808
I am trying to see if I can change Mvc routing to use a a ticket based system where the route is an guid string to an external dictionary of values. So I setup a new route like so:
routes.Add(new Route("{ticketid}", new Shared.TicketRouteHandler()));
And created a new route handler:
public class TicketRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new TicketHttpHandler(requestContext);
}
}
And a new HttpHandler class. In the ProcessRequest
method, I read the values for the controller and action out of the ticket, and place them into the route data collection:
string ticketidString = this.Request.RouteData.GetRequiredString("ticketid");
var ticket = Shared.Ticket.GetTicket(ticketId);
foreach (var item in ticket)
{
Request.RouteData.Values[item.Key] = item.Value;
}
The controller is executed like normal from there.
In the controller I have it looking for a ticketid parameter and if not found it creates one, and does a RedirecToAction, populating the new Ticket with the controller / action values:
public ActionResult Index(Guid? ticketid)
{
var ticket = Shared.Ticket.GetOrCreateTicket(ticketid);
if (ticketid == null)
{
//push the new values into the ticket dictionary instead of the route
string controller = "Home";
string action = "Index";
ticket.SetRoute(controller, action);
return RedirectToAction(action, controller, new { ticketid = ticket.Id });
}
return View();
}
This is all working fine, the problem is the URL that is created from the redirect action looks like this:
http://localhost:49952/df3b9f26-6c1c-42eb-8d0d-178e03b7e6f6?action=Index&controller=Home
Why is ?action=Index&controller=Home
getting attached to the url?
If I remove the extra pieces and refresh the page, everything loads perfectly from the ticket collection.
I imagine that it's happening after the HttpHandler code is finished.
What can I do to stop this behavior?
Upvotes: 2
Views: 4024
Reputation: 17808
After digging around the mvc4 source, I think I found a solution:
Derive a new type from RedirectToRouteResult
, override ExecuteResult
and modify the url generation to only return the ticket string:
public class TicketRedirectResult : RedirectToRouteResult
{
public override void ExecuteResult(ControllerContext context)
{
string destinationUrl = UrlHelper.GenerateUrl(
RouteName,
null /* actionName */,
null /* controllerName */,
//Only return the ticket id, not the entire dictionary
new RouteValueDictionary(new { ticketid = RouteValues["ticketid"] }),
Routes,
context.RequestContext, false /* includeImplicitMvcValues */);
// snip other code
}
}
Then in your controller override RedirectToAction
to return an instance of the new derived type:
protected override RedirectToRouteResult RedirectToAction(string actionName, string controllerName, RouteValueDictionary routeValues)
{
RouteValueDictionary mergedRouteValues;
if (RouteData == null)
{
mergedRouteValues = MergeRouteValues(actionName, controllerName, null, routeValues, includeImplicitMvcValues: true);
}
else
{
mergedRouteValues = MergeRouteValues(actionName, controllerName, RouteData.Values, routeValues, includeImplicitMvcValues: true);
}
//Only change here
return new TicketRedirectResult(mergedRouteValues);
}
Now the redirects only populates the ticket portion in the url.
Upvotes: 0
Reputation: 6839
The RedirectToAction method makes a new request and changes the URL in the browser:
This part is changing the URL:
return RedirectToAction(action, controller, new { ticketid = ticket.Id });
1) Return View doesn't make a new requests, it just renders the view without changing URLs in the browser's address bar.
2) Return RedirectToAction makes a new requests and URL in the browser's address bar is updated with the generated URL by MVC.
3) Return Redirect also makes a new requests and URL in the browser's address bar is updated, but you have to specify the full URL to redirect
4) Between RedirectToAction and Redirect, best practice is to use RedirectToAction for anything dealing with your application actions/controllers. If you use Redirect and provide the URL, you'll need to modify those URLs manually when you change the route table.
5) RedirectToRoute redirects to the specifies route defined in the the Route table.
But, View doesn't, try to adapt your code to return a View instead! Or, in the worst case, make something to handle the URL from the Response!
Upvotes: 3