热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用ASP.NETCore2.0建立规范的RESTAPIGET和POST

本文所需的一些预备知识可以看这里: http:www.cnblogs.comcgzlp9010978.html 和 http:www.cnblogs.comcgzlp9019314

本文所需的一些预备知识可以看这里: http://www.cnblogs.com/cgzl/p/9010978.html 和 http://www.cnblogs.com/cgzl/p/9019314.html

本文介绍的是使用ASP.NET Core建立Richardson成熟度为2级的伪RESTful web API, 本文介绍的是GET和POST.

使用的项目是(右键另存为, 然后把后缀名改为zip): https://images2018.cnblogs.com/blog/986268/201805/986268-20180516191053536-1701412182.jpg

首先, 资源应该使用名词, 它是个东西, 不是动作.

例如:

  • api/getusers 就是不正确的.
  • GET api/users 就是正确的
  • GET api/users/{userId}.

所以资源应该使用的是名词.

如果是非分层结构的资源, 那么它不应该这样命名: api/xxx/xxx/users, 而应该使用 api/users.

如果是单个资源, 不应该这样 api/id/users, 而应该是 api/users/{userId}.

(资源名是否复数还是根据个人习惯吧).

命名应该可以体现资源的结构

例如 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这个资源, 请参考下面这个列表:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这里GET可以理解为获取(查询)资源, POST为添加资源, PUT为整体更新资源, PATCH为局部更新资源, DELETE为删除资源.

这里需要提的是后两个:

  • HEAD: 和GET差不多, 但是它不应该返回响应的body, 所有没有响应的payload. 它主要使用来获取资源的一些信息, 例如查看资源是否可用等.
  • OPTIONS: 它是用来查询某个资源URI的可交互方式有哪些, 换句话说就是, 使用它可以知道某个URI是否可以执行GET或者POST动作, 这些结果通常是在响应的Headers里面而不是body里, 所以也没有响应的payload.
建立Controller

首先需要建立一个CountryController:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

注意在CountryController上面标注的[Route]属性标签,它的值是整个Controller下所有的Action的路由前缀,可以写成固定的地址,也可以写成"api/[controller]", 其中[controller]这部分会变成这个Controller的名字,这里也就是"api/country".

如果使用[controller]的话,如果Controller重构后名字改了,那么该Controller的路由地址也就是资源的地址也就改了,这样很不好,所以建议还是写成固定的地址不要使用[controller]。

GET 资源

GET 所有的Country:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

AutoMapper的使用方法这里就不介绍了

 GET 一个Country:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

 

这两个方法里返回的都是JsonResult,这看起来没什么问题,因为我们想要的就是JSON格式的结果。以第二个方法为例,使用POSTMAN测试,如果能查询到数据:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这是没有问题的,但是如果查询一个不存在的资源:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这就有问题了,如果查询不到资源,那么返回的应该是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()方法。

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这时就不需要手动返回JsonResult了。

这里需要注意的是,针对集合的内容协商,如果集合是空的,也不应该返回404,因为这个Country资源是存在的,只不过它的内容是空的而已。

然后看一下GET 特定单个资源:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

针对单个资源,如果没有找到,就需要返回404 Not Found,这时就可以使用Controller的帮助方法 NotFound().

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

 

处理异常

当Action发生异常的时候,默认情况下ASP.NET Core会返回500:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

但还是自己处理一下比较好,可以在Action里面使用try catch:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这里由于是服务器的错误,所以应该返回500状态码 Internal Server Error。

注意这里不应该返回Exception,因为这是程序的内部实现细节,再说它对客户来说也没什么用。

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

此外,我们还可以全局处理异常。

在Startup里面的Configure方法:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

使用app.UseExceptionHandler(),里面可以自定义一些逻辑。如果这地方代码比较多的话,可以把它封装成一个扩展方法,然后使用app.Usexxx的形式调用。

回头我把Action里面的try catch去掉试试,但是这里要注意把环境变量ASPNETCORE_ENVIRONMENT的值改成Production(其实不是Development就可以):

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

 

GET 父子关系的资源

这是一个典型的情景,一个国家包含多个城市,这就是父子关系。

首先看一下domain model:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这个应该很简单。

此外还要建立CityResource,Repository和IRepository,注册配置,种子数据等等,这些就不贴了。

下面建立CityController

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

前面提到过,针对父子、主从关系的资源,其子资源的路由地址应该是上面这样的,由于该Controller下所有的Action的路由前缀都是一样的,所以把这个路由放到了Controller级别作为所有Action的前缀。

而GET方法本身比较简单,没什么说的,里面涉及的一些方法请自行编写。

看看运行结果:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

如果找不到Country,则返回404:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

 

下面GET 单个city:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

注意,单个资源找不到就应该返回404,而空集合怎不是,这个前面也提过。

找到资源的结果:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

找不到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时:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

请求的Accept Header为application/xml时:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

它们返回的都是json格式的。

因为服务器(项目)现在不支持xml,所以返回了默认的json格式,但严格来说,这样做不正确,所以需要处理一下。

在Startup里,ConfigureServices方法:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

把这个ReturnHttpNotAcceptable属性设为true,如果想要的格式不支持,那么就会返回406 Not Acceptable:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

不指定Accept Header的情况下就返回默认的json格式:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

下面,为项目添加Xml输出格式的支持:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

再试试:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这时就成功的返回了xml。

 

创建资源

首先了解一下方法的安全性和幂等性。

安全性是指方法执行后并不会改变资源的表述。

幂等性是指方法无论执行多少次都会得到同样的结果。

下面是HTTP方法的安全性和幂等性列表:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

参考这个列表可以帮助决定在某种情况下用哪种HTTP方法。

下面看看创建Country的代码:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这个代码很简单,数据是从请求的body带进来的。

需要注意的是返回什么,如果POST操作执行成功的话,标准的做法是返回201 Created 状态码。

在这里就可以使用CreatedAtRoute() 这个方法,它允许响应里带着Location Header,在这个Location Header里包含着一个uri,通过这个uri就可以GET到我们刚刚创建好的资源(Country)。

这个方法的第一个参数是一个路由名,使用这个路由名可以用来生成刚才提到的uri。在本例里,这个路由名应该对应的是GetCountry这个Action方法,所以为这个Action添加路由名:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这样就和Post方法返回中用到的路由名一致了,第二个参数是一个匿名类里面有个属性id,它会编程路由里的参数,最后一个参数是响应会返回的数据。

下面进行测试,发送请求的时候别忘了设置Content-type为applicaiton/json:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

然后是数据:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

然后发送请求,查看响应的body部分:

 用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

再看响应的header:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这里可以看到Location Header的uri,通过这个uri,你就可以GET到这个刚刚创建的Country资源,这里我就不测试了。

 

如果再次执行这个POST操作,看看结果:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这次返回的数据的id为6,与前面不一样,所以POST不是幂等的,它每次执行后的结果是不一样的。

 

创建子资源

Country的创建做完了,现在可以创建City了。

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这个跟上面的差不多,只不过注意需要一下路由的参数即可。

测试:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

 

同时创建父子资源

这是个常见的需求,一个Country和它下属的Cities同时被传递进来,然后在Action里一同创建。

首先需要修改CountryAddResource:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

然后,就没有然后了,所有的映射操作都交给AutoMapper和EntityFramework Core了。。

测试:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

然后GET这两个Cities:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

 

创建集合资源

这次我要一次性添加一个集合的Countries。

由于Country的集合相当于是另外一种资源,所以可以把它放到单独的Controller里面,不放也没问题。

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

这个其实也没什么特别的,注意传进来的参数是IEnumerable。为了方便,暂时先返回OK()。

测试:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

OK, 下面解决返回的问题.

我们要返回的是CreatedAtRoute方法, 由于里面要包含可以返回该集合资源的路由地址, 所以需要创建一个Action, 它的参数应该是POST方法返回数据的Id的集合. 但是由于路由参数不支持集合形式, 只能以字符串形式传递, 所以可以做成这样的路由参数: api/xx/(1,2,3,4,5).

而Action方法呢, 接受的参数应该是Id的集合, 应该是一个集合类型, 所以我们可以使用ModelBinder把id字符串转化为id的集合:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

然后, 还需要对应这个POST Action 做一个GET集合的Action 方法:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

 

这个Action所期待的参数类型是Id的集合, 而实际传入的是id的字符串, 通过ArrayModelBinder来实现转化.

最后修改POST方法的返回:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

测试一下:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

然后再GET这个链接:

 用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

OK

 

如果POST到单个资源的地址

如果POST到这个地址 http://localhost:5000/api/countries/{id}, 

那么, 如果该id的资源不存在, 则应该返回404;

如果该id的资源存在, 则应该返回409 Conflict.

(POST不是幂等性的, 它无法多次请求都产生同样的结果).

测试一下id的资源不存在的情况:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

在测试一下Id的资源存在的情况:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

还是404, 这个不行, 所以需要手动处理:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

看看结果:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

OK, 无论是Id存在的资源还是不存在的资源都会返回正确的状态码.

 

支持输入其它类型的Content-Type

之前讲过如何返回xml的格式, 下面介绍一下如何使用xml格式进行请求, 首先在Startup.cs里面添加这个:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

然后, 需要把请求的Content-Type设为application/xml:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST

我就不适用xml数据进行测试了.

 

这次先到这, 随后会写DELETE, UPDATE, PATCH.

本文的源码地址: https://github.com/solenovex/ASP.NET-Core-2.0-RESTful-API-Tutorial


推荐阅读
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
author-avatar
qw874515
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有