问题
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¶m2=blah&etc
/api/1/search/html?param1=blah¶m2=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¶m2=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?
回答1:
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);
}
}
回答2:
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;
}
}
来源:https://stackoverflow.com/questions/7210200/customizing-asp-net-mvc-routing-to-service-json-xml-style-urls