开发者

Routing based on requested datatype in ASP.NET MVC

I'm trying to write REST-behaviour into my ASP.NET MVC2 app, but I'm having a hard time fig开发者_运维知识库uring out how to make the routes work as I want.

I'd like my routing to work like this:

/Users/Get/1 <- returns a regular HTML-based reply
/Users/Get.xml/1 <- returns the data from Get as XML
/Users/Get.json/1 <- returns the data as JSon

I've tried setting up routes like this:

routes.MapRoute("Rest", 
 "{controller}/{action}{format}/{id}" (...)

But it complains I need a separator between {action} and {format}

also the following:

routes.MapRoute("Rest",
     "{controller}/{action}.{format}/{id}" (...)

makes the /Users/Get/1 invalid (it needs to be /Users/Get./1 which is unacceptable)

Any suggestions?

-------------EDIT------------------------------------

I have one solution right now, but I'm not really happy with it:

routes.MapRoute(
            "DefaultWithFormat", // Route name
            "{controller}/{action}.{format}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", format = "HTML", id = UrlParameter.Optional } // Parameter defaults
        );
        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );

This works with both /Users/Get.whateverFormat/1 and also /Users/Get/1

Reason for this is that when I just do /Users/Get/1 (without the .format) it skips the first route, and goes to the next which doesn't include a format. To handle the return I've created an ActionFilterAttribute and override the OnActionExecuted method like this:

var type = filterContext.RouteData.Values["format"];
if (type != null && attributes != null)
{
    if (type == "HTML") return;
    if (type.ToString().ToLower() == "xml" && attributes.Any(a => a.AllowedTypes.Any(a2 => a2 == ResponseType.XML)))
    {
        filterContext.Result = new XmlResult(filterContext.Controller.ViewData.Model);
        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.ContentType = "text/xml";
        return;
    }
    if (type.ToString().ToLower() == "json" && attributes.Any(a => a.AllowedTypes.Any(a2 => a2 == ResponseType.JSON)))
    {
        filterContext.Result = new JsonResult() { Data = (filterContext.Controller.ViewData.Model), JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.ContentType = "text/json";
        return;
    }
}

And I also have a ResponseTypeAttribute which allows me to decorate the actions with what returntype they should allow:

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
public sealed class ResponseTypeAttribute : Attribute
{
    List<ResponseType> allowedTypes;

    public List<ResponseType> AllowedTypes
    {
        get { return allowedTypes; }
        set { allowedTypes = value; }
    }

    public ResponseTypeAttribute(params ResponseType[] allowedTypes)
    {
        this.allowedTypes = new List<ResponseType>();
        this.allowedTypes.AddRange(allowedTypes);
    }


}

public enum ResponseType
{
    XML, JSON
}

The XmlResult is just a simple object serializer.


Brad Wilson made a talk with title Advanced ASP.NET MVC2 where he has shown an example how to do exactly what you want. You can download the slides and example code here:

http://bradwilson.typepad.com/blog/talks.html

(It is the first talk on the page, and if I remember well restful urls is the first topic in the talk.)


Have you tried setting a default for {format} of html?


Maybe this is an option (using '/' instead of '.'):

routes.MapRoute(
        "Rest1",
        "Users/Get/{format}/{id}",
        new { controller = "Users", action = "Get", format = "HTML" }
        );

And then

public class UsersController : Controller {
   public ActionResult Get(string format, int id) {
      switch (format) {
         case "json":
            break;
         case "xml":
            break;
         default:
            break;
      }
      return new ContentResult(); // Or other result as required.
   }
}


Another idea:

routes.MapRoute(
    "Rest1",
    "Users/Get/{id}.{format}",
    new { controller = "Users", action = "Get", format = "HTML" }
    );

And then in controler method add some code for retreaving id and format


You could use regular expressions in your route, then define that "." as an optional character in your route.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜