众所周知,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的过渡有很多值得思考的地方。这篇文章里面借鉴了官方的一些资料和博客园张志敏的部分内容,一并表示感谢。