在WebAPI中,请求主体(HttpContent)只能被读取一次,不被缓存,只能向前读取的流。
举例子说明:
1. 请求地址:/?id=123&name=bob
服务端方法: void Action(int id, string name) // 所有参数都是简单类型,因而都将来自url
2. 请求地址:/?id=123&name=bob
服务端方法: void Action([FromUri] int id, [FromUri] string name) // 同上
void Action([FromBody] string name); //[FormBody]特性显示标明读取整个body为一个字符串作为参数
3. 请求地址: /?id=123
类定义:
public class Customer { // 定义的一个复杂对象类型
public string Name { get; set; }
public int Age { get; set; }
}
服务端方法: void Action(int id, Customer c) // 参数id从query string中读取,参数c是一个复杂Customer对象类戏,通过formatter从body中读取
服务端方法: void Action(Customer c1, Customer c2) // 出错!多个参数都是复杂类型,都试图从body中读取,而body只能被读取一次
服务端方法: void Action([FromUri] Customer c1, Customer c2) // 可以!不同于上面的action,复杂类型c1将从url中读取,c2将从body中读取
4.ModelBinder方式:
void Action([ModelBinder(MyCustomBinder)] SomeType c) // 标示使用特定的model binder来解析参数
[ModelBinder(MyCustomBinder)] public class SomeType { } // 通过给特定类型SomeType声明标注[ModelBidner(MyCustomBinder)]特性使得所有SomeType类型参数应用此规则
void Action(SomeType c) // 由于c的类型为SomeType,因而应用SomeType上的特性决定其采用model binding
总结:
1. 默认简单参数都通过URL参数方式传递,例外:
1.1 如果路由中包含了Id参数,则id参数通过路由方式传递;
1.2 如果参数被标记为[FromBody],则可以该参数可以为简单参数,客户端通过POST方式传递:$.ajax(url, '=value'),或者$.ajax({url: url, data: {'': 'value'}});
2. 默认复杂参数(自定义实体类)都通过POST方式传递,例外:
2.1 如果参数值被标记为[FromUri], 则该参数可以为复杂参数;
3. 被标记为[FromBody]的参数只允许出现一次, 被标记为[FromUri]的参数可以出现多次,如果被标记为[FromUri]的参数是简单参数,该标记可以去掉。
在客户端调用WebApi的一些接口时,最常使用到的HTTP方式有Get, Post, Put, Delete四个,下面会将这四个分成两类进行说明
第一类:Get/Delete
Delete在Body中传递参数有争议, 因此强烈建议只在URL中传递参数,所以这个分类里面只考虑通过URL传递参数的情况。
在URL中可以传递多个参数,但总长度有URL长度限制,参数之间使用&分隔,如以下格式:http://localhost:12345/api/Echo?arg1=value1&arg2=value2&arg3=中文
,其中的中文等特殊字符需要使用urlencode进行转义,在WebApi中接收的参数可以是多个单独的参数,也可以是由多个参数封装的对象(这个转换会由框架自动完成)
例如:http://localhost:12345/api/Echo/Single?arg1=1&arg2=2
http://localhost:12345/api/Echo/Multiple?arg1=1&arg2=2&strArg1=hello&strArg2=world
[RoutePrefix("api/Echo")]
public class EchoController : ApiController
{
[Route("Single")]
public void Get2([FromUri]int arg1, [FromUri]int arg2)
{ }
[Route("Muliple")]
public void Get3([FromUri]int arg1, [FromUri]int arg2, [FromUri]Arg arg3)
{ }
}
public class Arg
{
public string strArg1 { get; set; }
public string strArg2 { get; set; }
}
第二类:Post/Put
Post和Put在参数传递时既可以在URL中传递,也可以在Body中传递,当然一起传也是可以的,URL传递符合上面所描述的情况,下面着重说一下在Body中传值的情况。
Body正文只接收一个参数的传递,但不限该参数是一个值还是一个对象,所以如果想要在Body中传递多个参数,需要将这多个参数封装成一个对象
一个值参数
Api中定义的接收方式(还有多种接收参数的方式,这里只是用这一种方式进行举例):
[Route("Single")]
public void Post1([FromBody]int arg1)
{
}
Body中的正文:
1
多个值参数
Api中定义的接收方式:
[Route("Single")]
public void Post1([FromBody]int arg1, [FromBody]int arg2)
{
}
Body中的正文(以json格式为例):
{
"arg1":1,"arg2":2
}
此种方式会导致服务器报500错误,因为服务器端不能正确解析所传参数
对象参数:
Api中定义的接收方式:
[Route("SingleObject")]
public void Post2([FromBody]Arg arg)
{
}
Body中正文内容(以json为例,若要以form方式传输,请参考form格式):
{
"strArg1":"hello", "strArg2":"world"
}
若有多传的值会被自动忽略掉
Body正文格式
Body正文格式中分json和form两种传递方式,若使用form方式传递,请将http头Content-Type
设置为application/x-www-form-urlencoded
, 其中的x-www-form-urlencoded
为正文指定编码方式,而使用json形式传递, 请将http头Content-Type
设置为application/json; charset=utf-8
,参数正文不需要使用特殊的编码处理,不过强烈建议使用json这种传输方式,下面是两个分别使用form和json传递的http包:
Form方式
POST /api/test HTTP/1.1
Connection: keep-alive
Content-Length: 35
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Host: localhost:12345
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
arg1=1&arg2=2&content=%E6%B5%8B%E8%AF%95
Json方式
POST /api/test HTTP/1.1
Connection: keep-alive
Content-Length: 35
Content-Type: application/json; charset=UTF-8
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Host: localhost:12345
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
{
"arg1":1,
"arg2":2,
"content":"测试"
}