在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
本文是针对ASP.NET WepApi 2 的笔记。 1.void 2.HttpResponseMessage 3.IHttpActionResult 4.其他类型
路由路由表:Web API的默认路由模板"api/{controller}/{id}",此模板中,"api"是文本路径段,{controller}和{id}是占位符变量。 路由变体:可以显示指定Action的HTTP方法,如HttpGet,HttpPost。 public class ProductsController : ApiController { [HttpGet] public Product FindProduct(id) {} } 若要允许多个HTTP方法或GET,POST,PUT,DELETE以外的HTTP方法,可以显示使用AcceptVerbs属性: public class ProductsController : ApiController { [AcceptVerbs("GET", "HEAD")] public Product FindProduct(id) { } // WebDAV method [AcceptVerbs("MKCOL")] public void MakeCollection() { } } 可以通过ActionName属性重写Action名称: public class ProductsController : ApiController { [HttpGet] [ActionName("Thumbnail")] public HttpResponseMessage GetThumbnailImage(int id); [HttpPost] [ActionName("Thumbnail")] public void AddThumbnailImage(int id); } 也可以使用NonAction属性指定Action不参与路由匹配: // Not an action method. [NonAction] public string GetPrivateData() { ... } Web API路由过程的某些部分提供了扩展点:
若要为这些接口提供自己的实现,在HttpConfiguration对象上操作,HttpConfigutation在App_Start文件夹下的WebApiConfig.cs文件下能找到,也可以自己获取: var config = GlobalConfiguration.Configuration; config.Services.Replace(typeof(IHttpControllerSelector), new MyControllerSelector(config)); 属性路由:Route是API 2支持的一种新类型的路由,顾名思义,它可以使用属性定义路由: [Route("customers/{customerId}/orders")] public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... } 你可能需要先启用,才能使用属性路由: using System.Web.Http; namespace WebApplication { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API routes config.MapHttpAttributeRoutes(); // Other Web API configuration not shown. } } } 路由名称: 在Web API 中,每个路由都有一个名称。路由名称可用于生成链接,以便可以在HTTP响应中包含一个链接。 Route可以设置名称属性,下面的代码演示如何设置路由名称,以及如何使用根据路由名称生成的链接: public class BooksController : ApiController { [Route("api/books/{id}", Name="GetBookById")] public BookDto GetBook(int id) { // Implementation not shown... } [Route("api/books")] public HttpResponseMessage Post(Book book) { // Validate and add book to database (not shown) var response = Request.CreateResponse(HttpStatusCode.Created); // Generate a link to the new book and set the Location header in the response. string uri = Url.Link("GetBookById", new { id = book.BookId }); response.Headers.Location = new Uri(uri); return response; } } 路由顺序:属性路由还可以指定路由匹配时的顺序,RouteOrder,默认顺序是0,首先计算较低的值。 媒体格式化媒体类型,也称MIME类型。在HTTP中,媒体类型描述消息正文的格式。例如:
当HTTP消息包含实体正文时,Content-type标头指定消息正文的格式。 例如,如果HTTP响应包含PNG图像,响应可能包含以下标头: HTTP/1.1 200 OK Content-Length: 95267 Content-Type: image/png 当客户端发送请求时,它可以包含Accept标头。Accept标头指示客服端想要从服务器获取的响应媒体类型。例如: Accept: text/html,application/xhtml+xml,application/xml 此标头指示客户端希望获得HTML、XHTML或XML。 媒体类型可以确定Web API 如何序列化和反序列化HTTP消息正文。可以通过编写自己的媒体类型达到扩展Web API序列化和反序列化的目的。 若要创建媒体格式化程序,需要实现以下类:
下面的实例演示的媒体类型可以序列化以逗号分割值(CSV)格式的产品对象。下面是Product对象的定义: namespace ProductStore.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } } } 若要实现CSV格式化程序,定义一个类,派生自BufferedMediaTypeFormatter: namespace ProductStore.Formatters using System; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; using ProductStore.Models; namespace ProductStore.Formatters { public class ProductCsvFormatter : BufferedMediaTypeFormatter { } } 在构造函数中,添加格式化程序支持的媒体类型。在此示例中,支持类型"text/csv": public ProductCsvFormatter() { // Add the supported media type. SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv")); } 重写CanWriteType方法,以指示该类型格式化程序可以序列化: public override bool CanWriteType(System.Type type) { if (type == typeof(Product)) { return true; } else { Type enumerableType = typeof(IEnumerable<Product>); return enumerableType.IsAssignableFrom(type); } } 在此示例中,格式化程序可以序列化单个Product对象的集合、Product对象。 同样,重写CanReadType方法,以指示该类型格式化程序可以反序列化。在此示例中,格式化程序不支持反序列化,因此,此方法返回false: public override bool CanReadType(Type type) { return false; } 最后,重写WriteToStream方法。此方法通过向流写入序列化程序。如果支持反序列化,还可以重写ReadFromStream方法。 public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content) { using (var writer = new StreamWriter(writeStream)) { var products = value as IEnumerable<Product>; if (products != null) { foreach (var product in products) { WriteItem(product, writer); } } else { var singleProduct = value as Product; if (singleProduct == null) { throw new InvalidOperationException("Cannot serialize type"); } WriteItem(singleProduct, writer); } } } // Helper methods for serializing Products to CSV format. private void WriteItem(Product product, StreamWriter writer) { writer.WriteLine("{0},{1},{2},{3}", Escape(product.Id), Escape(product.Name), Escape(product.Category), Escape(product.Price)); } static char[] _specialChars = new char[] { ',', '\n', '\r', '"' }; private string Escape(object o) { if (o == null) { return ""; } string field = o.ToString(); if (field.IndexOfAny(_specialChars) != -1) { // Delimit the entire field with quotes and replace embedded quotes with "". return String.Format("\"{0}\"", field.Replace("\"", "\"\"")); } else return field; } 将自定义的媒体格式化程序添加到Web API管道 public static void ConfigureApis(HttpConfiguration config) { config.Formatters.Add(new ProductCsvFormatter()); } WebAPI 提供了对JSON和XML的媒体类型格式化程序。 JSON格式由JsonMediaTypeFormatter类处理,默认情况下JsonMediaTypeFormatter使用Json.NET库以执行序列化。 如果您愿意,可以配置JsonMediaTypeFormatter,以使用DataContractJsonSerializer而不是Json.NET。若要执行此操作,设置UseDataContractJsonSerializer属性true: var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.UseDataContractJsonSerializer = true; 默认情况下,所有公共属性和字段会参与序列化JSON。若要忽略属性或字段,给它装饰JsonIgnore属性。 public class Product { public string Name { get; set; } public decimal Price { get; set; } [JsonIgnore] public int ProductCode { get; set; } // omitted } 你也可以使用DataContract属性装饰你的类,存在此属性,则将忽略所有成员,除非它们具有DataMember(JsonProperty)属性。此外,可以使用DataMember序列化私有成员。 [DataContract] public class Product { [DataMember] public string Name { get; set; } [DataMember] public decimal Price { get; set; } public int ProductCode { get; set; } // omitted by default } 默认情况下,只读属性参与序列化。 默认情况下,Json.NET将日期将采用ISO 8601格式。例如: 2012-07-27T18:51:45.53403Z // UTC 2012-07-27T11:51:45.53403-07:00 // Local 您可以通过设置 DateTimeZoneHandling 属性覆盖此: var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc; 如果想要使用Microsoft JSON 日期格式( var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat; 编写缩进的JSON,可以如下设置: var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented; JSON属性名称使用驼峰式,无需修改数据模型,设置CamelCasePropertyNamesContractResolver序列化程序: var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 可以返回一个匿名对象。 XML媒体类型格式化程序 默认XmlMediaTypeFormatter类。默认使用DataContractSerializer来执行序列化。 若要执行此操作,设置UseXmlSerializer属性设置为true: var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; xml.UseXmlSerializer = true; 请考虑使用XmlSerializer如果你需要匹配现有 XML 架构。 介绍下DataContractSerializer的序列化行为:
当存在此属性时,类被序列,如下所示:
删除JSON或XML格式化程序若要执行此操作的主要原因是:
调用此成员,从你应用程序_启动Global.asax 中定义的方法。 void ConfigureApi(HttpConfiguration config) { // Remove the JSON formatter config.Formatters.Remove(config.Formatters.JsonFormatter); // or // Remove the XML formatter config.Formatters.Remove(config.Formatters.XmlFormatter); } ASP.NET Web API 中的模型验证当客户端将数据发送到 web API 时,通常要执行任何处理之前验证数据。 数据注释请考虑以下模型:
using System.ComponentModel.DataAnnotations; namespace MyApi.Models { public class Product { public int Id { get; set; } [Required] public string Name { get; set; } public decimal Price { get; set; } [Range(0, 999)] public double Weight { get; set; } } } Range属性指出 假设客户端发送 POST 请求,其中以下 JSON 表示形式: { "Id":4, "Price":2.99, "Weight":5 } 在你的控制器操作,可以检查模型是否有效: using MyApi.Models; using System.Net; using System.Net.Http; using System.Web.Http; namespace MyApi.Controllers { public class ProductsController : ApiController { public HttpResponseMessage Post(Product product) { if (ModelState.IsValid) { // Do something with the product (not shown). return new HttpResponseMessage(HttpStatusCode.OK); } else { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState); } } } } 可以创建一个Action筛选器来调用控制器操作前的模型检查: using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using System.Web.Http.ModelBinding; namespace MyApi.Filters { public class ValidateModelAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { if (actionContext.ModelState.IsValid == false) { actionContext.Response = actionContext.Request.CreateErrorResponse( HttpStatusCode.BadRequest, actionContext.ModelState); } } } } 在这种情况下,不会调用控制器操作。 若要将此筛选器应用于所有的 Web API 控制器,将添加到筛选器的实例HttpConfiguration.Filters在配置期间的集合: public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Filters.Add(new ValidateModelAttribute()); // ... } } 另一个选项是设置筛选器作为特性上各个控制器或控制器操作: public class ProductsController : ApiController { [ValidateModel] public HttpResponseMessage Post(Product product) { // ... } } ASP.NET Web API 中的参数绑定Web API 在控制器上调用方法时,必须设置参数的值,此过程称为“绑定”。 默认情况下,Web API 使用以下规则进行参数绑定:
例如,下面是典型的 Web API 控制器方法: HttpResponseMessage Put(int id, Product item) { ... }
item 参数是复杂类型,因此 Web API 使用媒体类型格式化程序从请求正文中读取值。 使用 [FromUri]下面的示例定义 public class GeoPoint { public double Latitude { get; set; } public double Longitude { get; set; } } public ValuesController : ApiController { public HttpResponseMessage Get([FromUri] GeoPoint location) { ... } } 使用 [FromBody]若要强制 Web API 从请求正文读取简单类型,请向参数添加 [FromBody] 特性: public HttpResponseMessage Post([FromBody] string name) { ... }
ASP.NET Web API 中的异常处理HttpResponseException默认情况下,大多数异常将转换为状态代码 500 内部服务器错误的 HTTP 响应。 例如,以下方法将返回 404,找不到,如果id参数无效。 public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return item; } 更好地响应的控制,还可以构造的整个响应消息,并将其与包含HttpResponseException: public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { var resp = new HttpResponseMessage(HttpStatusCode.NotFound) { Content = new StringContent(string.Format("No product with ID = {0}", id)), ReasonPhrase = "Product ID Not Found" }; throw new HttpResponseException(resp); } return item; } 异常筛选器HttpResponseException类型是一种特殊情况,因为它专为返回的 HTTP 响应。
编写异常筛选器的最简单方法是从派生System.Web.Http.Filters.ExceptionFilterAttribute类并重写OnException方法。 下面是将转换的筛选器NotImplementedException异常转化为 HTTP 状态代码 501,未实现: namespace ProductStore.Filters { using System; using System.Net; using System.Net.Http; using System.Web.Http.Filters; public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext context) { if (context.Exception is NotImplementedException) { context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented); } } } } 响应的属性HttpActionExecutedContext对象包含将发送到客户端的 HTTP 响应消息。 注册异常筛选器有几种方法来注册 Web API 异常筛选器:
筛选器应用到特定的Action,请将作为属性的筛选器添加到操作: public class ProductsController : ApiController { [NotImplExceptionFilter] public Contact GetContact(int id) { throw new NotImplementedException("This method is not implemented"); } } 筛选器应用到所有控制器上的操作,将筛选器作为属性添加到控制器类: [NotImplExceptionFilter] public class ProductsController : ApiController { // ... } 在此集合中的异常筛选器适用于任何 Web API 控制器操作。 GlobalConfiguration.Configuration.Filters.Add( new ProductStore.NotImplExceptionFilterAttribute()); 如果使用"ASP.NET MVC 4 Web 应用程序"项目模板来创建你的项目,将 Web API 配置代码内的放 public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Filters.Add(new ProductStore.NotImplExceptionFilterAttribute()); // Other configuration code... } } HttpError下面的示例演示如何返回 HTTP 状态代码 404 (找不到) 与HttpError响应正文中。 public HttpResponseMessage GetProduct(int id) { Product item = repository.Get(id); if (item == null) { var message = string.Format("Product with id = {0} not found", id); return Request.CreateErrorResponse(HttpStatusCode.NotFound, message); } else { return Request.CreateResponse(HttpStatusCode.OK, item); } } 在内部, CreateErrorResponse创建HttpError实例,然后创建HttpResponseMessage ,其中包含HttpError.
HttpError 和模型验证public HttpResponseMessage PostProduct(Product item) { if (!ModelState.IsValid) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState); } // Implementation not shown... } 此示例中可能会返回以下响应: HTTP/1.1 400 Bad Request Content-Type: application/json; charset=utf-8 Content-Length: 320 { "Message": "The request is invalid.", "ModelState": { "item": [ "Required property 'Name' not found in JSON. Path '', line 1, position 14." ], "item.Name": [ "The Name field is required." ], "item.Price": [ "The field Price must be between 0 and 999." ] } } 使用 HttpResponseException HttpError这样就可以返回强类型化模型中正常成功的情况下,同时仍返回HttpError如果出现错误: public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { var message = string.Format("Product with id = {0} not found", id); throw new HttpResponseException( Request.CreateErrorResponse(HttpStatusCode.NotFound, message)); } else { return item; } } 全局错误处理 ASP.NET Web API 2 中如今,在Web API中没有简单的方式记录或处理全局错误。可以通过异常筛选器过滤某些未经处理的异常,但也有一部分异常筛选器不能处理。例如:
我们可以使用异常筛选器,消息处理程序(后面会提到),但是它们都很难处理上面的问题,所以微软提供了两个类:IExceptionLogger和IExceptionHandler。 我们该怎么使用:
public interface IExceptionLogger { Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken); } public interface IExceptionHandler { Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken); }
|
请发表评论