我有一个现有的ASP.NET MVC网站,其行为如下:
public ActionResult Show(int id)
{
var customer = GetCustomer(id);
return View(new ShowCustomerModel(customer));
}
我现在需要能够将这些操作作为API的一部分执行,该API将从第三方应用程序调用。理想情况下,操作如下:
public ActionResult Get(int id)
{
var customer = GetCustomer(id);
return Json(new CustomerResource(customer));
}
问题是,存在哪些ASP.NET MVC工具或模式允许我将它们组合在一起 - 例如,Rails允许我指定多种返回格式:
def index
@customer = get_customer(...)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @customer}
format.json { render :json => @customer}
end
end
这是一个很好的模式吗?我应该只是:
public Customer Get(int id)
{
return GetCustomer(id);
}
并使用动作过滤器有选择地呈现为JSON或视图?
答案 0 :(得分:8)
我会创建一个足够智能的ActionResult
,根据提供的接受标题来确定您想要的结果类型,如下所示:
public class AdaptiveActionResult : ActionResult
{
private readonly object _model;
public AdaptiveActionResult(object model)
{
_model = model;
}
public override void ExecuteResult(ControllerContext context)
{
var accept = context.HttpContext.Request.AcceptTypes;
if (accept == null || !accept.Contains("application/json"))
{
var viewResult = new ViewResult {ViewData = new ViewDataDictionary(_model)};
viewResult.ExecuteResult(context);
return;
}
var jsonResult = new JsonResult
{
Data = _model,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
jsonResult.ExecuteResult(context);
return;
}
}
这可以扩展到检查他们是否需要XML,RSS,Atom,以及它是什么。
然后你可以在你的控制器中这样做:
public ActionResult Index()
{
return new AdaptiveActionResult(new MyModel());
}
答案 1 :(得分:6)
您可以设置自定义操作过滤器属性,以便在操作执行时捕获内容类型并将其存储到参数中:
public class ContentTypeRequestFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.ActionParameters["contentType"] =
filterContext.HttpContext.Request.ContentType;
base.OnActionExecuting(filterContext);
}
}
然后在您的控制器中,您可以修饰您的操作方法,添加参数,并对其进行检查:
[ContentTypeRequestFilter]
public ActionResult Get(int id, string contentType)
{
var customer = GetCustomer(id);
if(contentType.ToLower() == "application/json")
{
return Json(new CustomerResource(customer));
}
return View(new ShowCustomerModel(customer));
}
如果需要,您可以从那里适应其他内容请求类型(xml等)。我在Building a REST API architecture in MVC 3上的博文中采用了类似的方法。
答案 2 :(得分:3)
它可能不是您想要的,但您可以检查请求类型并采取相应的行动。
public ActionResult Get(int id)
{
if (Request.IsAjaxRequest)
{
// do stuff
return new JsonResult(new { Foo = "Foo", Bar = "Bar" });
}
else
{
// do stuff
return View(myModel);
}
// if you need beyond IsAjaxRequest, you could
// check the controller's Request object for other
// indicators as to what type of result to
// send back
}
答案 3 :(得分:3)
@Schwarty's answer是最好的,如果你想坚持你的网站并做自动红宝石的事情。
但我建议你为API构建一个单独的前端。主要问题是版本控制 - 你真的不应该发布一个不可版本化的api。我们阅读网页的人可以轻松调整到更新的数据模型。也许并不容易,但是当事情发生变化时我们通常不会打破。但是,API的消费者通常与您的特定实现相关联,并且不喜欢更改。
简短版本:它们是单独的任务,在大多数情况下应单独处理。
答案 4 :(得分:1)
您可以为每个操作设置3个操作并创建自定义属性并创建ActionInvoker,就像HttpPost和HttpGet的工作方式一样
[JsonRequest]
[ActionName("Get")]
public ActionResult GetAsJson(int id) {
//return as Json
}
[XmlRequest]
[ActionName("Get")]
public ActionResult GetAsXml(int id) {
//return as xml
}
public ActionResult Get(int id) {
//get stuff normally
}
显然需要一些编码来实现,但可以重复使用。
另一种方法是创建一个CustomerResult对象,只需要一个Action,并按照上面提到的那样做,但是这个逻辑可以放到你的CustomerResult对象中。