Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Customizing ASP.NET MVC Routing to service ".json" ".xml" Style Urls

I have a search Api I'm working on that needs to return search results in a block of Html (using styles the client has defined on their end). I would also like to return results in Json, for future Api stuff we'll eventually be using. Currently, the routes look like this:

/api/1/search/json?param1=blah&param2=blah&etc
/api/1/search/html?param1=blah&param2=blah&etc

For reference, the pattern here is /{area}/1/{controller}/{action}.

I like the look of some Api's I've seen that return results in different formats depending on the 'extension' they have in the url, a la:

/api/1/search.json?param1=blah&param2=blah&etc

However, I haven't figured out how to configure Asp.Net's Mvc routing to support this style. The general routing in ApiAreaRegistration.cs is:

context.MapRoute(
    "Api_default",
    "Api/1/{controller}/{action}/{id}",
    new { action = "Index", id = UrlParameter.Optional });

I have tried the following, defined above the general one, which doesn't work:

//search api
context.MapRoute(
    "searchJson",
    "api/1/{controller}.{action}",
    new { controller = "SearchController" });

How would I configure routing to enable the .format-style urls?

like image 795
Dusda Avatar asked Aug 26 '11 20:08

Dusda


People also ask

What is custom routing in MVC?

In the custom routing mode MVC sites respond to incoming requests using standard ASP.NET routing. Page URLs are determined by the routes that you register into your MVC application's routing table.

What is URL Routing in MVC?

In MVC, routing is a process of mapping the browser request to the controller action and return response back. Each MVC application has default routing for the default HomeController. We can set custom routing for newly created controller.

What is routing in MVC with example?

Basically, Routing is a pattern matching system that monitor the incoming request and figure out what to do with that request. At runtime, Routing engine use the Route table for matching the incoming request's URL pattern against the URL patterns defined in the Route table.


2 Answers

context.MapRoute(
    "Api_default",
    "{area}/1/{controller}.{format}",
    new { action = "Index", id = UrlParameter.Optional });

is probably what you want. Then you could just return the different results based on the argument passed in.

Within the context of an Api Area, the SearchController would look like this:

public class SearchController : Controller
{
    public ActionResult Index(string format, SearchModel search)
    {
        var results = searchFacade.SearchStuff(search);

        if(format.Equals("xml"))
            return Xml(results); //using an XmlResult or whatever
        if(format.Equals("html"))
            return View(results);
        return Json(results, JsonRequestBehavior.AllowGet);
    }
}
like image 99
sontek Avatar answered Oct 24 '22 02:10

sontek


The routing is a bit tricky because you're inserting a required parameter after an optional paramter - in general, I recommend using the Accept-Type header, which is both more RESTful and less tricky to route. However, with certain clients this could be problematic.

The routing would have to take the form with id and without id into account:

context.MapRoute(
    "Api_default",
    "Api/1/{controller}/{action}/{id}.{format}",
    new { action = "Index" });

context.MapRoute(
    "Api_default_2",
    "Api/1/{controller}/{action}.{format}",
    new { action = "Index" });

Since the results are typically not different except for the output serialization, you might not want to route to different actions. A custom ActionResult could be helpful. This way, the different serialization logic can be centralized and is easy to extend.

public class RestResult<T> : ActionResult
{
    public T Data { get; set; }

    public RestResult(T data)
    {
        Data = data;
    }

    private string SerializeToJson()
    {
        MemoryStream ms = new MemoryStream();
        YourFavouriteJsonSerializer.SerializeToStream(Data, Data.GetType(), ms);
        var temp = Encoding.UTF8.GetString(ms.ToArray());
        return temp;
    }     

    public override void ExecuteResult(ControllerContext context)
    {
        string resultString = string.Empty;
        string resultContentType = string.Empty;

        // alternatively, use the route value dictionary
        // or the accept-type, as suggested.
        var extension = SomeExtensionParserMethod(context.RequestContext.HttpContext.Request.RawUrl);
        string result = string.Empty;
        if (extension == "json")
        {
            result = SerializeJson()
        }
        else if(...)
        // etc

                    context.RequestContext.HttpContext.Response.Write(resultString);
        context.RequestContext.HttpContext.Response.ContentType = resultContentType;
    }
}
like image 35
mnemosyn Avatar answered Oct 24 '22 03:10

mnemosyn