Is it possible to change the querystring variable in ASP.NET MVC path before it hits the controller?
I have a controller method in ASP.NET MVC that looks like this:
public ActionResult GetAlbumPictures(int albumId)
{
var album = AlbumRepo.GetSingle(albumId);
var pictures = album.Pictures;
return View(pictures);
}
The routing for this method looks like this:
routes.MapRoute(null,
"pictures"
new { controller = "Album", action = "GetAlbumPictures" });
The user will use the following URL to get the pictures, filtered by the album ID:
GET http://server/pictures?albumid=10
However, I'd like to change the querystring parameter to just album
instead of albumid
:
GET http://server/pictures?album=10
This would mean that the controller method needs to be modified to:
public ActionResult GetPictures(int album)
{
...
}
However, this is not ideal because now the method has a parameter named album
, which can be confused as an Album
object instead of the ID
of the Album
.
My question is, is there any way of configuring ASP.NET MVC so that in the routing, it will receive a querystring parameter called album
, but then pass it off to the controller as the albumId
parameter?
P.S. I know that I can do this in the routing table:
routes.MapRoute(null,
"album/{albumId}/pictures",
new { controller = "A开发者_StackOverflow社区lbum", action = "GetAlbumPictures" });
But due to legacy issues, I have to make it work for the querystring method as well.
You can create a custom action filter attribute to handle this scenario. I haven't tested this specific implementation, but the general idea is to do something like this:
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);
}
}
Then, decorate your action method with the [Album] attribute:
[Album]
public ActionResult GetAlbumPictures(int albumId)
{
var album = AlbumRepo.GetSingle(albumId);
var pictures = album.Pictures;
return View(pictures);
}
You can use a Custom Model Binder and it will work with both album and albumId. It can be implemented as follows:
Custom Model Binder:
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;
}
}
Action implementation:
public ActionResult GetAlbumPictures
([ModelBinder( typeof( AlbumModelBinder ) )] int albumId)
{
var album = AlbumRepo.GetSingle(albumId);
var pictures = album.Pictures;
return View(pictures);
}
Global.asax.cs implementation:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.Add( typeof( int ), new AlbumModelBinder() );
RegisterRoutes( RouteTable.Routes );
}
Consider intercepting your request and use HttpContext.RewritePath to alter the query string before its picked up by the Routing engine. There is a nice diagram here showing how Url Rewriting is executed ages before the routing :)
http://learn.iis.net/page.aspx/496/iis-url-rewriting-and-aspnet-routing/
You can intercept the request before it hits the controller in Application_BeginRequest in your global.asax. You won't have access to the MVC contexts though, but could use
Server.Transfer(...);
or
Response.Redirect(...);
Response.End();
精彩评论