自定义ASP.NET MVC路由以服务“.json”“。xml”样式URL

时间:2011-08-26 20:23:55

标签: c# asp.net-mvc asp.net-mvc-routing

我正在进行搜索Api,需要在一个Html块中返回搜索结果(使用客户端在其末尾定义的样式)。我还想在Json中返回结果,以便我们最终将使用的未来Api内容。目前,路线看起来像这样:

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

作为参考,此处的模式为/ {area} / 1 / {controller} / {action}。

我喜欢一些Api的外观,我看到返回的结果会有不同的格式,具体取决于它们在网址中的“扩展名”,la:

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

但是,我还没有想出如何配置Asp.Net的Mvc路由来支持这种风格。 ApiAreaRegistration.cs中的常规路由是:

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

我已尝试过以下定义的一般内容,但不起作用:

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

如何配置路由以启用.format样式的网址?

2 个答案:

答案 0 :(得分:9)

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

可能就是你想要的。然后你可以根据传入的参数返回不同的结果。

在Api区域的上下文中,SearchController将如下所示:

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

答案 1 :(得分:6)

路由有点棘手,因为你在可选的参数后面插入了一个必需的参数 - 一般来说,我建议使用Accept-Type标题,这样更加RESTful且更少路线很棘手。但是,对某些客户而言,这可能会有问题。

路由必须将带有id且没有id的表单考虑在内:

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

由于除输出序列化外,结果通常没有差异,因此您可能不希望路由到不同的操作。自定义ActionResult可能会有所帮助。这样,不同的序列化逻辑可以集中化,并且易于扩展。

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