本文介绍的是使用ASP.NET Core创建Richardson成熟度为2级的伪RESTful web API, 本文介绍的是GET和POST.git
使用的项目是(右键另存为, 而后把后缀名改成zip): https://images2018.cnblogs.com/blog/986268/201805/986268-20180516191053536-1701412182.jpggithub
RESTful API 资源 (Resource) 的命名指导规范
首先, 资源应该使用名词, 它是个东西, 不是动做.web
例如:数据库
- api/getusers 就是不正确的.
- GET api/users 就是正确的
- GET api/users/{userId}.
因此资源应该使用的是名词.编程
若是是非分层结构的资源, 那么它不该该这样命名: api/xxx/xxx/users, 而应该使用 api/users.json
若是是单个资源, 不该该这样 api/id/users, 而应该是 api/users/{userId}.api
(资源名是否复数仍是根据我的习惯吧).安全
命名应该能够体现资源的结构
例如 api/department/{departmentId}/emoloyees, 这就表示了department (部门)和 员工(employee)以前是主从关系.服务器
而 api/department/{departmentId}/emoloyees/{employeeId}, 就表示了该部门下的某个员工.
而过滤, 排序等不是资源, 因此这样写 api/users/orderby/username 是不正确的.
过滤排序这类的参数是能够做为查询参数传递进来的, 正确的写法应该是: api/users?orderby=username.
可是有时候, RPC风格的方法调用很难映射成规范的资源命名, 因此有时能够打破规范 例如 api/users/{userId}/totalsalaries.
应该使用什么类型做为ID
若是使用int型做为ID的话, 大部分时候是没有问题的, 可是若是您使用的数据库的ID是自增整型的, 若是你替换数据库了, 而后把原有数据迁移到新数据库了, 那么现有数据的ID就会发生变化, 那么至关于全部的资源的地址发生了变化, 这就违反了这个:
资源的URI应该永远都是同样的.
因此GUID应该做为ID来使用. (可是我为了省事, 仍是使用自增int做为ID吧😂).
使用GUID做为主键的好处就是:
- 能够切换数据库
- 必定层度上隐藏了内部实现细节
经过HTTP方法与资源交互
针对项目里的Country这个资源, 请参考下面这个列表:
这里GET能够理解为获取(查询)资源, POST为添加资源, PUT为总体更新资源, PATCH为局部更新资源, DELETE为删除资源.
这里须要提的是后两个:
- HEAD: 和GET差很少, 可是它不该该返回响应的body, 全部没有响应的payload. 它主要使用来获取资源的一些信息, 例如查看资源是否可用等.
- OPTIONS: 它是用来查询某个资源URI的可交互方式有哪些, 换句话说就是, 使用它能够知道某个URI是否能够执行GET或者POST动做, 这些结果一般是在响应的Headers里面而不是body里, 因此也没有响应的payload.
创建Controller
首先须要创建一个CountryController:
注意在CountryController上面标注的[Route]属性标签,它的值是整个Controller下全部的Action的路由前缀,能够写成固定的地址,也能够写成"api/[controller]", 其中[controller]这部分会变成这个Controller的名字,这里也就是"api/country".
若是使用[controller]的话,若是Controller重构后名字改了,那么该Controller的路由地址也就是资源的地址也就改了,这样很很差,因此建议仍是写成固定的地址不要使用[controller]。
GET 资源
GET 全部的Country:
(AutoMapper的使用方法这里就不介绍了)
GET 一个Country:
这两个方法里返回的都是JsonResult,这看起来没什么问题,由于咱们想要的就是JSON格式的结果。以第二个方法为例,使用POSTMAN测试,若是能查询到数据:
这是没有问题的,可是若是查询一个不存在的资源:
这就有问题了,若是查询不到资源,那么返回的应该是404 NOF FOUND 而不是200 OK.
状态码
状态码是很是重要的,由于只有状态码会告诉API的消费者:
- 请求是否如预期的成功,或者失败
- 若是出现了错误,谁该为这个错误负责
下面再列举一下web API会用到的状态码:
200级别,表示成功:
- 200 - OK
- 201 - Created,表示资源建立成功了
- 204 - No content,成功执行,可是不该该返回任何东西
400级别,表示客户端引发的错误:
- 400 - Bad request,表示API的消费者发送到服务器的请求是错误的
- 401 - Unauthorized,表示没有权限
- 403 - Forbidden,表示用户验证成功,可是该用户仍然没法访问该资源
- 404 - Not found,表示请求的资源不存在
- 405 - Method not allowed,这就是当咱们尝试发送请求给某个资源时,使用的HTTP方法倒是不容许的,例如使用POST api/countries, 而该资源只实现了 GET,因此POST不被容许
- 406 - Not acceptable,这里涉及到了media type,例如API消费者请求的是application/xml格式的media type,而API只支持application/json
- 409 - Conflict,表示该请求没法完成,由于请求与当前资源的状态有冲突,例如你编辑某个资源数据之后,该资源又被其它人更新了,这时你再PUT你的数据就会出现409错误;有时也用在尝试建立资源时该资源已存在的状况。
- 415 - Unsupported media type,这个和406正好返回来,好比说我向服务器提交数据的media type是xml的,而服务器只支持json,那么就会返回415
- 422 - Unprocessable entity,表示请求的格式没问题,可是语义有错误,例如实体验证错误。
500级别,服务器错误:
- 500 - Internal server error,这表示是服务器发生了错误
回到刚才的那两个方法,默认状况下 JsonResult会返回200 OK状态码,能够去修改JsonResult以支持其它的状态码。可是Controller里提供了一些帮助方法返回IActionResult并指定特定的状态码,针对200,就是Ok()方法。
这时就不须要手动返回JsonResult了。
这里须要注意的是,针对集合的内容协商,若是集合是空的,也不该该返回404,由于这个Country资源是存在的,只不过它的内容是空的而已。
而后看一下GET 特定单个资源:
针对单个资源,若是没有找到,就须要返回404 Not Found,这时就可使用Controller的帮助方法 NotFound().
处理异常
当Action发生异常的时候,默认状况下ASP.NET Core会返回500:
但仍是本身处理一下比较好,能够在Action里面使用try catch:
这里因为是服务器的错误,因此应该返回500状态码 Internal Server Error。
注意这里不该该返回Exception,由于这是程序的内部实现细节,再说它对客户来讲也没什么用。
此外,咱们还能够全局处理异常。
在Startup里面的Configure方法:
使用app.UseExceptionHandler(),里面能够自定义一些逻辑。若是这地方代码比较多的话,能够把它封装成一个扩展方法,而后使用app.Usexxx的形式调用。
回头我把Action里面的try catch去掉试试,可是这里要注意把环境变量ASPNETCORE_ENVIRONMENT的值改为Production(其实不是Development就能够):
GET 父子关系的资源
这是一个典型的情景,一个国家包含多个城市,这就是父子关系。
首先看一下domain model:
这个应该很简单。
此外还要创建CityResource,Repository和IRepository,注册配置,种子数据等等,这些就不贴了。
下面创建CityController
前面提到过,针对父子、主从关系的资源,其子资源的路由地址应该是上面这样的,因为该Controller下全部的Action的路由前缀都是同样的,因此把这个路由放到了Controller级别做为全部Action的前缀。
而GET方法自己比较简单,没什么说的,里面涉及的一些方法请自行编写。
看看运行结果:
若是找不到Country,则返回404:
下面GET 单个city:
注意,单个资源找不到就应该返回404,而空集合怎不是,这个前面也提过。
找到资源的结果:
找不到country或者city的时候都应该返回404,就不贴图了。
内容协商
简单来讲就是,若是资源支持多种展示格式,那么消费者能够选择它想要的格式。
这里就要用到media type,它能够经过请求的Accept Header来传递,常见的有:
application/json 和 application/xml...等等
在没有指定Accept Header的状况下,就该返回一个默认的格式,在ASP.NET Core 2.0里面就是application/json。
当请求的media type不可用的时候,而且消费者不支持默认格式,这时服务器就应该返回 406 Not Acceptable 状态码。
ASP.NET Core 支持输出和输入两种格式化器。
输出的media type在accept header里面,而输入的media type在content-type header里面。
看一下当前的状况,请求的Accept Header为application/json时:
请求的Accept Header为application/xml时:
它们返回的都是json格式的。
由于服务器(项目)如今不支持xml,因此返回了默认的json格式,但严格来讲,这样作不正确,因此须要处理一下。
在Startup里,ConfigureServices方法:
把这个ReturnHttpNotAcceptable属性设为true,若是想要的格式不支持,那么就会返回406 Not Acceptable:
不指定Accept Header的状况下就返回默认的json格式:
下面,为项目添加Xml输出格式的支持:
再试试:
这时就成功的返回了xml。
建立资源
首先了解一下方法的安全性和幂等性。
安全性是指方法执行后并不会改变资源的表述。
幂等性是指方法不管执行多少次都会获得一样的结果。
下面是HTTP方法的安全性和幂等性列表:
参考这个列表能够帮助决定在某种状况下用哪一种HTTP方法。
下面看看建立Country的代码:
这个代码很简单,数据是从请求的body带进来的。
须要注意的是返回什么,若是POST操做执行成功的话,标准的作法是返回201 Created 状态码。
在这里就可使用CreatedAtRoute() 这个方法,它容许响应里带着Location Header,在这个Location Header里包含着一个uri,经过这个uri就能够GET到咱们刚刚建立好的资源(Country)。
这个方法的第一个参数是一个路由名,使用这个路由名能够用来生成刚才提到的uri。在本例里,这个路由名应该对应的是GetCountry这个Action方法,因此为这个Action添加路由名:
这样就和Post方法返回中用到的路由名一致了,第二个参数是一个匿名类里面有个属性id,它会编程路由里的参数,最后一个参数是响应会返回的数据。
下面进行测试,发送请求的时候别忘了设置Content-type为applicaiton/json:
而后是数据:
而后发送请求,查看响应的body部分:
再看响应的header:
这里能够看到Location Header的uri,经过这个uri,你就能够GET到这个刚刚建立的Country资源,这里我就不测试了。
若是再次执行这个POST操做,看看结果:
此次返回的数据的id为6,与前面不同,因此POST不是幂等的,它每次执行后的结果是不同的。
建立子资源
Country的建立作完了,如今能够建立City了。
这个跟上面的差很少,只不过注意须要一下路由的参数便可。
测试:
同时建立父子资源
这是个常见的需求,一个Country和它下属的Cities同时被传递进来,而后在Action里一同建立。
首先须要修改CountryAddResource:
而后,就没有而后了,全部的映射操做都交给AutoMapper和EntityFramework Core了。。
测试:
而后GET这两个Cities:
建立集合资源
此次我要一次性添加一个集合的Countries。
因为Country的集合至关因而另一种资源,因此能够把它放到单独的Controller里面,不放也没问题。
这个其实也没什么特别的,注意传进来的参数是IEnumerable。为了方便,暂时先返回OK()。
测试:
OK, 下面解决返回的问题.
咱们要返回的是CreatedAtRoute方法, 因为里面要包含能够返回该集合资源的路由地址, 因此须要建立一个Action, 它的参数应该是POST方法返回数据的Id的集合. 可是因为路由参数不支持集合形式, 只能以字符串形式传递, 因此能够作成这样的路由参数: api/xx/(1,2,3,4,5).
而Action方法呢, 接受的参数应该是Id的集合, 应该是一个集合类型, 因此咱们可使用ModelBinder把id字符串转化为id的集合:
而后, 还须要对应这个POST Action 作一个GET集合的Action 方法:
这个Action所期待的参数类型是Id的集合, 而实际传入的是id的字符串, 经过ArrayModelBinder来实现转化.
最后修改POST方法的返回:
测试一下:
而后再GET这个连接:
OK
若是POST到单个资源的地址
若是POST到这个地址 http://localhost:5000/api/countries/{id},
那么, 若是该id的资源不存在, 则应该返回404;
若是该id的资源存在, 则应该返回409 Conflict.
(POST不是幂等性的, 它没法屡次请求都产生一样的结果).
测试一下id的资源不存在的状况:
在测试一下Id的资源存在的状况:
仍是404, 这个不行, 因此须要手动处理:
看看结果:
OK, 不管是Id存在的资源仍是不存在的资源都会返回正确的状态码.
支持输入其它类型的Content-Type
以前讲过如何返回xml的格式, 下面介绍一下如何使用xml格式进行请求, 首先在Startup.cs里面添加这个:
而后, 须要把请求的Content-Type设为application/xml:
我就不适用xml数据进行测试了.
此次先到这, 随后会写DELETE, UPDATE, PATCH.
本文的源码地址: https://github.com/solenovex/ASP.NET-Core-2.0-RESTful-API-Tutorial
转载,原文地址: https://www.cnblogs.com/cgzl/p/9047626.html
写的很好,记录下来,留做下次开发的标准