两种内置的序列化器
Web API为我们内置了两种序列化器,即JSON和XML,具体使用哪一个依请求头中Accept值来决定。Accept用来指定响应内容的格式(也称媒体类型),常见的媒体类型有以下几个,如图。
1.为什么请求同一个资源,IE和Chrome得到的结果不一样呢?
默认情况下,请求同一个资源,比如:GET api/contact/searchcontactbyid/001,IE拿到的是JSON,而Chrome 拿到的即是XML,这是为什么呢?
仔细分析就会发现,是它们的请求头的Accept不一样导致的, Chrome显式指定了application/xml,而IE没有显示指定。
2.自定义序列化器
我们也可以自定义一个序列化器加到Web API中,步骤如下。
①定义一个序列化器继承MediaTypeFormatter或BufferedMediaTypeFormatter。
②将序列化器加入到Web API处理管道中,方法如下图。
定制序列化
对于默认的JSON和XML序列化器,我们可以对其行为定制,比如控制哪些字段可以被序列化,日期格式,缩进等等。下面就以JSON序列化器为例来介绍。
1.忽略字段
默认情况下,所有的共有字段和属性都会被序列化。如果想忽略其中某个字段,有两种方法可以实现。
①方法1,使用JsonIgnore特性,下面代码中字段ProductCode就不会被序列化,如下图。
②方法2,除了使用json.net自带的特性外,我们还可以使用内置的方法,命名空间System.Runtime.Serialization下的DataContract和DataMember,如果想忽略某个字段,不给它加DataMember特性就可以。当然,内置方法与JsonIgnore不同的地方在于,DataMember还可以序列化私有成员。
2.是否缩进
可以通过指定json.net的formatting选项来决定是否缩进,默认值是None即不缩进,如果要其结果缩进可以指定值为Indented,下图是不缩进和缩进的对比。
图1:不缩进
图2:缩进
模型绑定
Web API中模型绑定非常类似于MVC,分模型验证和参数绑定两个阶段。
1.模型验证
当接受到一个请求时,通常需要对其数据进行验证,验证通过后才进行处理。我们可以使用Web API内置的验证机制来实现,包括数据注解和错误处理。
①数据注解
可以使用命名空间System.ComponentModel.DataAnnotations下的特性来标注,比如我希望模型Contact的FirstName不能为null,加上特性Required即可,如下图。
②错误处理
假设我要往服务端插入一条新记录,客户端发送请求POST api/contact/addcontact,请求的JSON数据如下图。
服务端action代码如下
通过调试会发现,模型绑定后FirstName为null,所以ModelState.IsValid返回false,如下图。
图1:FirstName为null
图2:服务端返回的内容
上面我们是在action内部做的验证和错误处理,实际上,我们可以通过创建一个filter,在调用action之前就进行验证和错误处理,步骤如下。
①定义filter,如图1
②应用filter,有两种方式,一是刚定义的filter实例加到HttpConfiguration.Filters集合中,如图2。二是在Controller或Action上应用特性,如图3。
图1:定义filter
图2:将filter加到HttpConfiguration.Filters中
图3:在Action上应用filter
2.参数绑定
默认情况下,有两种类型的绑定参数,简单类型(见备注)和自定义类型。对于简单类型,Web API会从URI(包括querystring和route data)中取值;对于自定义类型,Web API会从请求体(request body)中取值。
注:简单类型,即常见的string,int,bool,datetime等以及能转换成string的类型。
①简单类型绑定
对于简单类型Web API会从route data或query string中取值,如下图。
GET api/Contact/SearchContactById/001 //route data
GET api/Contact/SearchContactById?id=001 //query string
这两个uri都能映射到这个action:
②自定义类型绑定
对于自定义类型Web API会从request body中取值,示例代码如下。
Request:
Action:
Response:
收到415的错误,错误信息如下图。
错误日志已经明确告诉我们是由于没有在request header中指定content-type造成的,这里会引申另一个知识点,Web API中的内容协商。简单来说就是,客户端告诉服务端返回什么格式的数据,然后服务端按客户端的要求返回相应格式数据的过程。通常,客户端会在请求头中指定需要的格式,主要的选项如下。
a.Accept:可接受的媒体类型,如application/json,application/xml等
b.Accept-Charset:可接受的字符集,如UTF-8,ISO 8859-1等
c.Accept-Encoding:可接收的内容编码,如“gzip”。
d.Accept-Language:优先选用的自然语言,如“en-us”。
了解了内容协商后,我们知道需要在请求头中指定content-type的值。
Accept指定response body的格式,而Content-Type指定request body的格式,如果没有指定Accept,则使用Content-Type的格式。
客户端请求:
服务端响应:HTTP/1.1 200 OK
③定制参数绑定
我们可以改变Web API默认的参数绑定行为,如果要从URI中读取自定义类型,可以使用特性FromUri,如图1;如果要从request body中读取简单类型,可以使用特性FromBody,如图2。
图1
请求URI:GET http://localhost.dev.wingontravel.com/HWARestAPI/api/Contact/SearchContact?id=001&FirstName=san&LastName=zhang
图2
请求如下图
需要注意的是:FromUri可以被标注多次,但是FromBody只能被标注一次。