【问题标题】:Is it possible to change the querystring variable in ASP.NET MVC path before it hits the controller?是否可以在 ASP.NET MVC 路径中的查询字符串变量到达控制器之前更改它?
【发布时间】:2010-12-30 00:05:00
【问题描述】:

我在 ASP.NET MVC 中有一个控制器方法,如下所示:

public ActionResult GetAlbumPictures(int albumId)
{
    var album = AlbumRepo.GetSingle(albumId);
    var pictures = album.Pictures;
    return View(pictures);
}

此方法的路由如下所示:

routes.MapRoute(null,
                "pictures"
                new { controller = "Album", action = "GetAlbumPictures" });

用户将使用以下 URL 获取图片,按相册 ID 过滤:

GET http://server/pictures?albumid=10

但是,我想将查询字符串参数更改为 album 而不是 albumid

GET http://server/pictures?album=10

这意味着控制器方法需要修改为:

public ActionResult GetPictures(int album)
{
    ...
}

但是,这并不理想,因为现在该方法有一个名为album 的参数,它可能会被混淆为Album 对象,而不是AlbumID

我的问题是,有没有办法配置 ASP.NET MVC,以便在路由中,它会收到一个名为 album 的查询字符串参数,然后将其作为 albumId 参数传递给控制器​​?

P.S.我知道我可以在路由表中做到这一点:

routes.MapRoute(null,
                "album/{albumId}/pictures",
                new { controller = "Album", action = "GetAlbumPictures" });

但由于遗留问题,我必须使其也适用于查询字符串方法。

【问题讨论】:

    标签: c# asp.net-mvc routing query-string asp.net-mvc-3


    【解决方案1】:

    您可以创建自定义操作过滤器属性来处理这种情况。我没有测试过这个具体的实现,但总体思路是这样的:

    public class AlbumAttribute : ActionFilterAttribute
        {
             public override void OnActionExecuting(ActionExecutingContext filterContext)
             {
                 var albumId = filterContext.HttpContext.Request.QueryString["album"] as string;
                 filterContext.ActionParameters["albumId"] = albumId;
    
                 base.OnActionExecuting(filterContext);
             }
        }
    

    然后,用 [Album] 属性装饰您的操作方法:

    [Album]
    public ActionResult GetAlbumPictures(int albumId)
    {
        var album = AlbumRepo.GetSingle(albumId);
        var pictures = album.Pictures;
        return View(pictures);
    }
    

    【讨论】:

    • 它不起作用,因为在 AlbumAttribute 类中无法访问 Request.QueryString["album"]。
    • 其实是的。我已经更新了代码以反映访问它的正确方式。
    • 谢谢,这似乎是最优雅的解决方案。
    • 我需要在传递给所有操作之前更改查询字符串的值。我应该在哪里做而不是为每个操作添加操作过滤器属性?
    • @Leon:您可以在 Controller OnActionExecuting() 中更改它。这是我的代码:pastebin.com/uYCmfvXr
    【解决方案2】:

    您可以使用自定义模型绑定器,它适用于专辑和专辑 ID。 可以如下实现:

    自定义模型绑定器:

    public class AlbumModelBinder : IModelBinder
    {
        public object BindModel
        (ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            int albumId;
            var albumVar = bindingContext.ValueProvider.GetValue( "album" );
            if (albumVar != null)
            {
                albumId = int.Parse( albumVar.AttemptedValue );
            }
            else
            {
                albumId = int.Parse( bindingContext.ValueProvider.GetValue( "albumId" ).AttemptedValue );
            }
            return albumId;
        }
    }
    

    动作实施:

    public ActionResult GetAlbumPictures
    ([ModelBinder( typeof( AlbumModelBinder ) )] int albumId)
    {
        var album = AlbumRepo.GetSingle(albumId);
        var pictures = album.Pictures;
        return View(pictures);
    } 
    

    Global.asax.cs 实施:

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        ModelBinders.Binders.Add( typeof( int ), new AlbumModelBinder() );
        RegisterRoutes( RouteTable.Routes );
    }
    

    【讨论】:

    • 感谢您的回答。自定义模型绑定器会很好用,但对于像这种情况这样微不足道的事情,它只是比我想要的多一点工作。
    【解决方案3】:

    考虑拦截您的请求并使用 HttpContext.RewritePath 在路由引擎拾取查询字符串之前对其进行更改。这里有一个很好的图表,显示了 URL 重写是如何在路由之前执行的:)

    http://learn.iis.net/page.aspx/496/iis-url-rewriting-and-aspnet-routing/

    【讨论】:

      【解决方案4】:

      您可以在请求到达 global.asax 中 Application_BeginRequest 中的控制器之前拦截请求。您将无法访问 MVC 上下文,但可以使用

      Server.Transfer(...);
      

      Response.Redirect(...); 
      Response.End();
      

      【讨论】:

      • 在这种情况下为什么不使用 HttpContext.RewritePath 呢? IMO 非常适合这种情况,不是吗?
      猜你喜欢
      • 2012-07-14
      • 2015-02-26
      • 2012-09-03
      • 1970-01-01
      • 2017-03-23
      • 1970-01-01
      • 1970-01-01
      • 2018-08-25
      • 2020-07-19
      相关资源
      最近更新 更多