本节内容,涉及到6.1-6.6(P155-182),以WebApi说明为主。主要NuGet包:无
一、创建WebApi的最佳实践,综合了RPC和Restful两种风格的特点
1 //定义Person类和ErrorInfo类 2 public record Person(int Id, string Name, int Age); 3 public record ErrorInfo(int Code, string? Message); 4 5 6 //定义控制器和一个Get方法 7 [ApiController] 8 [Route("api/[controller]/[action]")] 9 public class TestController : ControllerBase 10 { 11 //模拟一个persons数据集合 12 private List<Person> persons = new List<Person>(); 13 public TestController() 14 { 15 var p1 = new Person(1, "ZS", 25); 16 var p2 = new Person(2, "LS", 18); 17 var p3 = new Person(3, "WU", 35); 18 var p4 = new Person(4, "ZL", 46); 19 persons.Add(p1); 20 persons.Add(p2); 21 persons.Add(p1); 22 persons.Add(p1); 23 24 } 25 26 [HttpGet("{id}")] 27 public ActionResult<Person> GetPersonById(int id) 28 { 29 if (id < 1) 30 { 31 return BadRequest(new ErrorInfo(1, "id必须是正数")); 32 } 33 else if(id > 4) 34 { 35 return NotFound(new ErrorInfo(2, "查无此人")); 36 } 37 else 38 { 39 return persons.FirstOrDefault(p => p.Id == id); 40 } 41 } 42 }
代码解读:
8行:统一设置控制器类的路径为[controller]/[action],控制器+方法名,前缀api可省略。这个方式倾向于RPC风格,可以直接知道API的意图
9行:因为不需要MVC中的视图功能,所以WebApi的控制器继承ControllerBase即可,ControllerBase中定义了Response、Request、HttpContext等属性成员来获取请求和响应信息,以及BadRequest、NotFound、OK等方法来快速设置响应报文。
26行:所有方法,都须加上Http方法的Attribute,主要有[HttpGet]、[HttpPost]、[HttpPut]、[HttpDelete],分别进行查询、新增、更新、删除操作。这不仅有利于明确操作方法的请求类型,也有利于使用Swagger+OpenApi来生成文档。使用Swagger时,如果没有标注,会报错,如果确定这个方法不生成Api,可以标注[ApiExplorerSettings(IgnoreApi = true)]
27行:返回值如果是普通类型,直接返回即可,会自动序列化为JSON格式,但如果返回值为复杂类型,需要使用ActionResult<T>泛型类型来包装
31,35行:使用BadRequest、NotFound、Ok等方法,快速设置响应数据,可以和ActionResult<T>很好的结合使用。其中BadRequest的响应状态码为400,NotFound为404,Ok为200。同时,方法参加为一个自定义ErrorInfo对象,包括错误码和错误信息两个属性,进一步说明具体的错误信息。方法会自动将ErrorInfo对象序列化为JSON对象
39行:直接返回对象,因为方法设置了返回值为ActionResult<T>,自动将复杂对象序列化为JSON对象。这里严谨一点,应该判断一下是否为null
补充说明:
①HTTP的四个常用请求谓词的特征:GET/Put/DELETE是幂等操作,网关或网络请求组件会对失败请求自动重试;POST是非幂等的,需要注意重复提交的情况;GET请求的响应可以被缓存,而其它请求不能被缓存;GET和DELETE不支持请求体传参,POST和PUT支持
②HTTP的常用状态码:401,需要身份认证的但是没有提供;403,需要权限的但没有权限;404,请求的资源不存在;400,请求参数错误或其它业务错误;200,请求处理成功
③如果方法中调用了异步方法,则返回值为async Task<ActionResult<T>>
二、Api方法参数的最佳实践
1、通过URL
//通过HttpGet的参数设置请求路径,其中{}内的,为路径参数 //如果方法参数的名称和{}内的不一样,则参数使用[FromRoute]指定 //如果请求参数schoolName为aaa,classNo为bbb //则请求路径为api/Test/GetAll/school/aaa/class/bbb [HttpGet("school/{schoolName}/class/{classNo}")] public ActionResult<Student[]> GetAll(string schoolName, [FromRoute(Name = "classNo")] string classNum){}
2、通过QueryString
//从Query中获取实参时,就不需要在HttpGet中设置路径 //如果请求参数名称和方法行参名称不同,则要指定[FromQuery]的Name属性 //假设请求参数schoolName为aaa,classNo为bbb //则请求地址为api/Test/GetAll?schoolName=aaa&classNum=bbb [HttpGet] public ActionResult<Student> GetAll(string schoolName, [FromQuery(Name = "classNo")]string classNum){}
3、通过请求体
//方法参数为复杂类型时,自动序列化为JSON,通过请求体发送 //参数中可加、可不加[FromBody] //请求地址为api/Test/AddNew [HttpPost] public ActionResult<Student> AddNew(Student studentDto) { persons.Add(studentDto); return Ok(studentDto); }
4、除了FromRoute、FromQuery、FromBody之外,还有FromForm、FromHeader等
5、传递参数最佳实践:
①GET和DELETE请求,参数使用QueryString
②POST请求,参数使用请求体
③PUT请求,定位参数使用QueryString,DTO参数使用请求体
④GET请求,如果参数内容超过URL长度限制,则把请求改为PUT,并通过请求体来传递参数
⑤Restful风格,要求路径参数用于定位资源,Query参数用于传递额外参数,但使用Query更符合中文习惯,所以URL还是比较少使用
⑥注意:请求报文头的Content-Type要设置为application/JSON