在ASP.NET MVC网站上构建API

时间:2011-11-17 22:44:30

标签: asp.net asp.net-mvc api rest

我有一个现有的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或视图?

5 个答案:

答案 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对象中。