2

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.

page loading without the extra querystring values

I imagine that it's happening after the HttpHandler code is finished.

What can I do to stop this behavior?

4

2 回答 2

3

RedirectToAction 方法发出一个新请求并更改浏览器中的 URL:

这部分正在更改 URL:

return RedirectToAction(action, controller, new { ticketid = ticket.Id });

1) Return View 不会发出新的请求,它只是呈现视图而不更改浏览器地址栏中的 URL。

2) Return RedirectToAction 发出一个新的请求,浏览器地址栏中的 URL 被 MVC 生成的 URL 更新。

3) Return Redirect 也会发出新的请求,并且浏览器地址栏中的 URL 会更新,但您必须指定完整的 URL 才能重定向

4) 在 RedirectToAction 和 Redirect 之间,最佳实践是使用 RedirectToAction 来处理任何处理您的应用程序操作/控制器的事情。如果您使用重定向并提供 URL,则在更改路由表时需要手动修改这些 URL。

5) RedirectToRoute 重定向到路由表中定义的指定路由。

但是, View 不会,尝试调整您的代码以返回 View !或者,在最坏的情况下,制作一些东西来处理来自响应的 URL!

于 2013-06-28T15:48:55.507 回答
0

在挖掘了mvc4源之后,我想我找到了一个解决方案:

从 派生一个新类型RedirectToRouteResult,覆盖ExecuteResult并修改 url 生成以仅返回票证字符串:

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
    }
}

然后在您的控制器中重写RedirectToAction以返回新派生类型的实例:

    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);
    }

现在重定向仅填充 url 中的票证部分。

于 2013-06-28T17:18:27.300 回答