剖析ASP.NET WebAPI 转化 ActionResult 为 HttpResponse的机制

剖析ASP.NET WebAPI 转化 ActionResult 为 HttpResponse的机制

众所周知,ASP.NET MVC 通过Action接收用户请求,返回的是ActionResult.ActionResult包含很多种类,这里我搜集了一些常见类型的ActionResult及其相关的描述,见下表格,这里需要指出的是ActionResult也是有类的继承体系的,并不是单一的类,稍后会给出ActionResult的继承体系图。

如上所述,众多ActionResult的类型能够根据请求选择合适的返回类型。对于传统的ASP.NET,请求的是物理文件,根据文件后缀去选择相应的HttpHandler进行处理,HttpHandler对于开发者来说相对比较封闭,如果要对请求做额外的处理,需要重写某些方法。现在使用MVC,Action根据请求灵活选择返回的内容和方式,整个控制都交给开发者来做。回到正题,ActionResult的继承体系如下:

从上面的图不难看出,ActionResult只是众多返回类型的根,从ASP.NET MVC 过渡到 ASP.NET WebAPI,对于请求的解析变化不大,变化最大的是返回类型,返回的是Http的一些状态(删除,新建,更新),对于获取对象的方法,通过HttpResponseMessage<T>封装一个对象返回去,这个稍微特殊点。ApiController当然也支持返回对象,通常是程序内部使用。上述情形的代码Demo如下:

1)通过HttpResponseMessage包装的返回对象

public HttpResponseMessage<product> PostProduct(Product product) {   this._dbContext.Save(product);   var result = new HttpResponseMessage<product>(product, HttpStatusCode.Created);   var location = Url.Route(null, new { id = product.ProductID });   result.Headers.Location = new Uri(location);   return result;}

2)直接返回实体

public IEnumerable<Product> GetAllProducts()  {  return products;  } 

对于WebApi的请求,可以归纳为四类,Create 、 Update 、 Read 、 Delete,下面列举了相关的匹配情况


HTTP 的四个主要方法 (GET, PUT, POST, DELETE) 按照下列方式映射为 CURD 操作:

  • GET 用于获取 URI 资源的进行展示, GET 操作不应对服务端有任何影响;
  • PUT 用于更新 URI 上的一个资源, 如果服务端允许, PUT 也可以用于新建一个资源;
  • POST 用于新建 资源, 服务端在指定的 URI 上创建一个新的对象, 将新资源的地址作为响应消息的一部分返回;
  • DELETE 用于删除指定的 URI 资源。

下面有一个实现的例子。

资源新建(Create)

public HttpResponseMessage<product> PostProduct(Product product) {   this._dbContext.Save(product);   var result = new HttpResponseMessage<product>(product, HttpStatusCode.Created);   var location = Url.Route(null, new { id = product.ProductID });   result.Headers.Location = new Uri(location);   return result;}

资源更新

public HttpResponseMessage PutProduct(int id, Product product) {   if (!this._dbContext.Products.Any(p => p.ProductID == id)) {      throw new HttpResponseException(HttpStatusCode.NotFound);   }   product.ProductID = id;   this._dbContext.Update(product);   return new HttpResponseMessage(HttpStatusCode.OK);}


资源删除

public HttpResponseMessage DeleteProduct(int id) {   var product = this._dbContext.Products.FirstOrDefault(p => p.ProductID == id);   this._dbContext.Delete(product);   return new HttpResponseMessage(HttpStatusCode.NoContent);}

说到这里,可以总结一下WebApi对于返回类型是如何转化为HttpResponse的。

1) Void类型

public class ValuesController : ApiController{    public void Post()    {    }}

客户端会收到如下信息

HTTP/1.1 204 No ContentServer: Microsoft-IIS/8.0Date: Mon, 27 Jan 2014 02:13:26 GMT

2)HttpResponseMessage

public class ValuesController : ApiController{    public HttpResponseMessage Get()    {        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");        response.Content = new StringContent("hello", Encoding.Unicode);        response.Headers.CacheControl = new CacheControlHeaderValue()        {            MaxAge = TimeSpan.FromMinutes(20)        };        return response;    } }

这个请求只是返回了一个状态,大多数时候,我们都是一些内容的。如下:

public HttpResponseMessage Get(){    // Get a list of products from a database.    IEnumerable<Product> products = GetProductsFromDB();    // Write the list to the response body.    HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, products);    return response;}

3)IHttpActionResult

这是WebApi2才引入的一个类型,这个接口定义了一个HttpResponseMessage的工厂,使用IHttpActionResult的好处主要有:

* 方便地写单元测试

* 封装了创建HttpResponse的细节

IHttpActionResult包含一个异步方法-ExecuteAsync,该方法创建一个HttpResponseMessage的实例。

public interface IHttpActionResult{    Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken);} 

下面是一个使用IHttpActionResult的例子

public class TextResult : IHttpActionResult{    string _value;    HttpRequestMessage _request;    public TextResult(string value, HttpRequestMessage request)    {        _value = value;        _request = request;    }    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)    {        var response = new HttpResponseMessage()        {            Content = new StringContent(_value),            RequestMessage = _request        };        return Task.FromResult(response);    }}


4)其他返回类型

public class ProductsController : ApiController{    public IEnumerable<Product> Get()    {        return GetAllProductsFromDB();    }}

总结:ASP.NET MVC 到ASP.NET Api的过渡有很多值得思考的地方。这篇文章里面借鉴了官方的一些资料和博客园张志敏的部分内容,一并表示感谢。

推荐阅读