.net 温故知新【12】:Asp.Net Core WebAPI 中的Rest风格

RPC

RPC(Remote Procedure Call),远程过程调用),这种RPC形式的API组织形态是类和方法的形式。所以API的请求往往是一个动词用来标识接口的意思,比如 https://xxxx/GetStudent?id=1https://xxxx/AddStudent 这种风格,并且往往没有规范需要我们去查看接口定义文档。HTTP方法基本只用GET和POST,没有使用HTTP的其它谓词设计比较简单。

Rest

Rest:按照Http的语义来使用HTTP协议的一种风格,Rest全称Representational State Transfer(表现层状态转换)。他是一种规范或者设计风格而不是特别的技术。REST形式的API组织形态是资源和实体,请求的路由可以看出对资源的访问,规范统一接口自解释。
比如 https://xxxx/Student/1 用Get方法调用就是获取编号为1的学生。 https://xxxx/Student/1 用Delete调用就是删除编号为1的学生,用delete调用就是删除该学生。
在HTTP中这些调用方法GET、POST、PUT、PATCH、DELETE 即HTTP谓词。GET用来获取资源,POST用来新建资源,PUT用来更新指定资源,PATCH用来批量更新资源,DELETE用来删除资源,通过谓词来表示请求动作或者意图,通过url定位资源。
在请求中GET、PUT、DELETE 请求是幂等的,也就是说可以重试请求。而POST不是幂等,因为POST意思是添加数据。
在Rest风格中使用状态码来标识返回结果,其中常用200、201、400、401、404、410、500等。

ASP.NET Core WebAPI接口

在ASP.NET WebAPI中我们也能看到Rest的风格,理想很丰满,显示很骨干。如果我们严格的按照Rest风格设计接口的话,需要对技术人员有很高的要求,需要去划分不同业务不同的资源定位,而且有些业务也找不到准确的谓词去定义,响应状态码有限无法表达准确的意思,或者是时间上来不及等等原因。
而且这种方式更符合国外语言表达的方式,不太适合我国宝宝体质。
所以我们在设计接口的时候不用非要用Rest风格,我们可以靠近或者在特定的更适合使用Rest接口系统中使用。
本篇我们结合Rest看下接口如何设计和交互。

  • 在http接口请求中有三种方式传递参数或者数据。
  1. URL:资源定位,也就是Rest风格,在请求的url中包含信息,比如https://xxxx/Student/1 1就是学生编号。
  2. QueryString: URL之外的额外信息,比如RPC中https://xxxx/GetStudent?id=1 id=1就是QueryString
  3. 请求报文体:供PUT、POST提交提供数据,请求体有多种格式application/x-www-form-urlencoded、multipart/form-data、application/json、text/plain、application/xml。
  • 返回状态码
    在RPC中Post请求我们习惯如果请求已经在服务器处理,不管处理结果是否正确,我们都返回200状态码。然后在返回数据中用其它信息来标识业务结果。比如{code:1,msg:"成功"}或者{code:0,msg:"失败"}
    而在Rest 中Post通常用201返回新增成功,delete 删除的数据不存在返回404,但是404大家知道可能也许是url错误,所以表诉不清。
    因此我们在实际设计中可能会进行Rest裁剪,我们既使用RPC的返回结果,同时多用准确的状态码,不用什么都返回200。
    使用RPC风格,尽量使用合理谓词,不知道使用什么谓词的时候就用POST,Get Delete参数尽量用资源定位URL,业务错误服务端返回合适的状体吗,不知道返回什么就返回400,如果请求处理成功就用200同时返回结果数据。

在上一篇中遗留的这个问题 .net 温故知新【11】:Asp.Net Core WebAPI 入门使用及介绍

image

所以我们在Controller中Route配置为[Controller]则不管方法接口名称是什么,仍然以Rest的方式访问。

    [Route("[controller]")]
    [ApiController]
    public class RestCutController : ControllerBase
    {
        [HttpGet]
        public IEnumerable<string> GetStudents()
        {
            //获取所有学生
            return new string[] { "student1", "student2" };
        }

        [HttpGet("{id}")]
        public string GetStudent(int id)
        {
            //获取id的学生
            return "student"+id;
        }

        [HttpPost]
        public void PostStudent([FromBody] string value)
        {
            //新增
        }

        [HttpPut("{id}")]
        public void PutStudent(int id, [FromBody] string value)
        {
            //修改
        }

        [HttpDelete("{id}")]
        public void DeleteStudent(int id)
        {
            //删除id学生
        }
    }

image

当我们修改Rout按照RPC方式,[Route("[controller]/[action]")] 运行后发现swagger展示的接口方式就改变了。并且保留了参数URL的方式。

image

关于返回状态码的问题可以有两种方式,一种是直接在ControllerBase.Response 响应中指定返回状态码。

        [HttpDelete("{id}")]
        public string DeleteStudent(int id)
        {
            //删除id学生
            if (id == 1)
            {
                return "删除成功";
            }
            else {

                Response.StatusCode = 404;
                return "未找到!";
            }
        }

另外一种方式就是返回泛型ActionResult<string>,其中OKNotFound是继承自ActionResult然后隐式转换到泛型,也可以直接返回IActionResult或者ActionResult但是类型不确定这样swagger文档就不会解析出返回值,所以我们用ActionResult泛型。

        [HttpDelete("{id}")]
        public ActionResult<string> DeleteStudent(int id)
        {
            //删除id学生
            if (id == 1)
            {
                return Ok("删除成功");
            }
            else
            {
                return NotFound("未找到!");
            }
        }

image

        [HttpDelete("{id}")]
        public ActionResult DeleteStudent(int id) //返回ActionResult
        {
            //删除id学生
            if (id == 1)
            {
                return Ok("删除成功");
            }
            else
            {
                return NotFound("未找到!");
            }
        }

image

最后我们在总结下关于API参数获取的方式,在 [HttpGet("{id}")]中我们看到有{id},这个就是占位符,从RUL中获取,不光可以配置占位符还可以配置路径的其它值,甚至可以随意组织,只要我们的参数明和占位符相同就行。

        [HttpDelete("number/{id}/Name/{name}")] //自己组织的URL
        public ActionResult<string> DeleteStudent(int id,string name)
        {
            //删除id学生
            if (id == 1)
            {
                return Ok("删除成功");
            }
            else
            {
                return NotFound("未找到!");
            }
        }

image

当然也可以使用[FromRoute]从route获取,另外我们还有一些Attribute用于从不同的地方获取参数,比如从QueryString获取。那么我的请求URL就应该是/RestCut/DeleteStudent?id=1

        [HttpDelete]
        public ActionResult<string> DeleteStudent([FromQuery] int id)
        {
            //删除id学生
            if (id == 1)
            {
                return Ok("删除成功");
            }
            else
            {
                return NotFound("未找到!");
            }
        }

image

最后还有[FromHeader][FromForm][FromBody]这些获取参数的方式,不清楚的使用的时候查询就行了。

posted @ 2023-07-13 09:39  XSpringSun  阅读(797)  评论(0编辑  收藏  举报