ASP.NET Core与RESTful API 开发实战

978-7-115-51951-1
作者: 杨万青
译者:
编辑: 张爽
分类: .NET

图书目录:

详情

ASP.NET Core是微软推出的新一代跨平台、高性能Web开发框架,具有模块化、内置依赖项注入、开源、易于部署等特点。作为近些年来主流的软件架构风格,REST旨在构建简单、可靠、高性能、高伸缩性的Web应用。 本书系统地介绍了如何使用ASP.NET Core开发RESTful API应用,共包含10章内容。前3章主要介绍了REST、HTTP、ASP.NET Core的基础理论。第4~10章讲述如何根据前3章的理论逐步构建规范的RESTful API应用,涉及资源的基本操作、Entity Framework Core、高级查询、日志、缓存、并发、HATEOAS、认证与安全、测试以及部署等内容。

图书摘要

版权信息

书名:ASP.NET Core与RESTful API 开发实战

978-7-115-51951-1 A20182001

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

著    杨万青

责任编辑 张 爽

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315


ASP.NET Core是微软推出的新一代跨平台、高性能Web开发框架,具有模块化、内置依赖项注入、开源、易于部署等特点。作为近些年来主流的软件架构风格,REST旨在构建简单、可靠、高性能、高伸缩性的Web应用。

本书系统地介绍了如何使用ASP.NET Core开发RESTful API应用,共包含10章内容。前3章主要介绍了REST、HTTP、ASP.NET Core的基础理论。第4~10章讲述如何根据前3章的理论逐步构建规范的RESTful API应用,涉及资源的基本操作、Entity Framework Core、高级查询、日志、缓存、并发、HATEOAS、认证与安全、测试以及部署等内容。

本书适合.NET开发者或.NET初学者阅读,也适合那些有其他编程语言基础且想要学习.NET Core的开发者阅读。同时,本书也适合作为大专院校计算机专业的师生用书和培训学校的教材。


微软于2016年推出.NET Core和ASP.NET Core。作为一个全新的开发平台,.NET Core对.NET Framework框架进行了重大的改进,解决了.NET Framework的一个非常明显的缺陷,实现了跨平台性。使用.NET Core能够开发出适用于各种平台以及各种不同类型的应用,如Web应用程序、微服务、控制台程序、Windows桌面应用(自.NET Core 3.0起)等,并能够轻松地部署到各个平台上。同时,.NET Core沿用了.NET Framework框架的优点,使.NET开发人员能够顺利上手并在工作中使用。.NET Core从发布之初就加入开源组织,并受到开发者社区的支持,目前.NET Core已经达到2.2版本,其成熟性、稳定性、高性能、模块化等特点将帮助开发者开发出适用于各种场景的企业级应用。

作为.NET Core平台的重要角色,ASP.NET Core旨在开发现代主流的Web应用程序。与.NET Core一样,ASP.NET Core同样具有跨平台、开源、模块化、高性能等特点,这些特点使它完全超越了ASP.NET。ASP.NET Core还具有一些重要特点,如内置依赖项注入、轻型的高性能模块化HTTP请求管道、灵活的配置与日志系统等,使开发者能够轻松地开发出更灵活、更安全和高质量的Web应用程序。同时,ASP.NET Core内置了对云平台和容器的支持,使它能够快速地部署到不同的平台中。

自从2000年Roy Thomas Fielding在其博士论文中首次提出REST后,REST一直就是人们讨论的话题,并且不断地应用于各种技术的实现中。作为一种软件架构风格,REST旨在构建简单、可靠、高性能、高伸缩性的Web应用。然而,由于它并不像标准一样具有详尽的定义、说明与规则,并且开发者易受其他Web服务开发风格的影响,因此多数人对于它的认识与理解不够全面,甚至存在一定程度上的误解。

本书以ASP.NET Core与REST为主题,介绍了如何使用ASP.NET Core开发出规范的RESTful API应用。本书不仅详细地介绍了REST、REST约束以及HTTP协议,还深入介绍了ASP.NET Core及其重要性。充分理解ASP.NET Core与REST有助于开发者设计出规范的RESTful API。

本书系统介绍ASP.NET Core与RESTful API应用的开发,共分为10章。前3章重点介绍理论知识,后7章主要讲述实践操作。前3章的理论为后7章的实践提供了支持,如第1章介绍的HTTP消息头和状态码、第3章介绍的ASP.NET Core核心特性,会在后7章中经常提及并用到。这种从理论到实践、由浅入深的学习方式有助于读者进一步掌握所学的内容。如果你刚开始学习.NET Core开发,建议按照章节顺序阅读本书。

从第4章开始的项目实践将带领读者一步一步地开发RESTful API应用,从项目的创建到实现对资源的操作,从使用Entity Framework Core到高级查询与日志,从为项目添加认证功能到为项目应用ASP.NET Core提供的各种安全特性,从测试到部署,这一系列内容贯穿了一个实际项目的整个开发流程。读者若能从头到尾实践本书中项目的开发流程,将会受益匪浅。同时,在介绍项目开发的过程中,对于遇到的新知识,本书也进行了必要的理论性介绍,如第4章中的仓储模式、第7章中的HTTP缓存、第8章中的CORS、第10章中的Docker与Azure等。

本书主要内容如下。

第1章介绍API与REST的基本概念、REST约束、HTTP协议,以及REST中资源表述常用的JSON格式与XML格式等。

第2章介绍.NET Core、.NET Standard以及ASP.NET Core,讨论ASP.NET Core自2.0后各个版本新增加的特性与变化,展示开发环境的设置以及如何开始创建第一个Web API应用。

第3章深入剖析ASP.NET Core提供的重要特性,如启动与Kestrel服务器、中间件、依赖注入、MVC、配置、日志、错误处理。

第4章介绍实例项目(该项目将贯穿本书后面的章节)的创建,讨论如何准备测试数据、仓储模式,以及如何实现对资源的各种操作,如获取、创建、删除、更新等,最后讨论内容协商及其实现方式。

第5章介绍Entity Framework Core以及如何在项目中使用它,并使用它替换原来的内存数据源方式,以及使用异步方式替换原来的同步方式。

第6章介绍分页、过滤、搜索、排序的实现,以及如何记录日志并处理异常。

第7章介绍较为复杂的主题,包括缓存以及不同种类缓存的实现方式、并发控制的实现方式、API版本、HATEOAS,以及GraphQL及其实现。

第8章介绍如何保护API应用程序,包括为应用程序添加认证功能、使用Identity保存用户信息、使用HTTPS与HSTS。该章还会讲解数据保护API和用户机密的概念与使用,以及跨域资源访问及其实现方式。

第9章介绍如何对应用程序进行单元测试、集成测试,并为其创建OpenAPI文档。

第10章介绍如何将应用程序部署到不同的位置,如IIS、Docker以及Azure,同时介绍Docker与Azure的概念与基本操作。

完成本书的写作是一件不容易的事。本书尽可能涵盖相关的知识点,并尽力确保内容的正确性,使读者能够从中有所收获。然而由于个人水平有限,书中疏漏之处在所难免,如果你在学习过程中发现书中的错误或对本书有任何建议和意见,既可以告诉作者或本书编辑,也可以提交到异步社区中,我们将非常感激。

作者邮箱:ictcm@outlook.com

编辑邮箱:zhangshuang@ptpress.com.cn

感谢我的家人在我写作期间给予我的支持和包容,也感谢那些帮助我解决疑问并给出建议的技术专家和同事。最后,感谢本书编辑为本书提供的指导与建议。


本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,单击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问http://www.epubit.com/ selfpublish/submissionwww.epubit.com/selfpublish/submission即可)。

如果您是学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。

异步社区

微信服务号


本章内容

REST,全称为REpresentational State Transfer,即表述性状态传递,它是一种应用程序的架构风格,用于构造简单、可靠、高性能的Web应用程序。REST提出了一系列约束,遵循这些约束的应用程序称为RESTful API应用。

要设计出REST风格的应用,开发者首先应该对REST及其相关概念有基本的理解。因此,本章首先介绍API以及REST的基本概念,并介绍REST所定义的架构约束,同时也会介绍常见的对REST的错误理解。

通常情况下,REST是基于HTTP协议而实现的,因此本章会重点介绍HTTP协议,包括媒体类型、HTTP方法、HTTP消息头和状态码等。之后,本章还会提出一些REST的最佳实践,指导开发者设计出优秀的RESTful应用。最后本章还会介绍RESTful API开发中的常见问题,如JSON与XML格式,以及API版本问题。

API全称Application Programming Interface,即应用程序编程接口,我们在开发应用程序时经常用到。API作为接口,用来“连接”两个不同的系统,并使其中一方为另一方提供服务,比如在操作系统上运行的应用程序能够访问操作系统所提供的API,并通过这些API来调用操作系统的各种功能。因此,API是一个系统向外暴露或公开的一套接口,通过这些接口,外部应用程序能够访问该系统。

在Web应用程序中,Web API具有同样的特性,它作为一个Web应用程序,向外提供了一些接口,这些接口的功能通常是对数据进行操作(如获取或修改数据等),它们能够被外部应用程序,比如桌面应用程序、手机应用甚至其他Web应用程序(如ASP.NET Core MVC视图应用、单页Web应用)等访问并调用。

Web API能够实现不同应用程序之间的访问,它与平台或编程语言无关,可以使用不同的技术来构建Web API,如Java、.NET等;调用方也不受Web API使用的平台或技术限制,比如一个使用Java语言开发的Android手机应用可以调用一个使用C#语言开发的ASP.NET Core Web API应用程序。同时,API作为接口,仅向外部应用程序提供了抽象,而不是在其内部实现。这正如高级编程语言中的接口(Interface)一样,调用方无须关心接口如何实现,仅需要调用它所提供的方法即可。此外,Web API由于其自身的特点,不能直接被用户使用,相反地,它通常由开发人员来使用。通过调用Web API,开发人员能够设计出丰富多样的应用。因此,设计良好、有丰富文档的Web API更易于被开发者接受并使用。

REST(REpresentational State Transfer)的意思是表述性状态传递,它是Roy Thomas Fielding在2000年发表的博士论文中提出来的一种软件架构风格。作为一种Web服务的设计与开发方式,REST可以降低开发的复杂性,提高系统的可伸缩性。

REST是一种基于资源的架构风格,在REST中,资源(Resource)是最基本的概念。任何能够命名的对象都是一个资源,如document、user、order等,通常情况下,它表示Web服务中要操作的一个实体。一个资源具有一个统一的资源标识符(Uniform Resource Identifier,URI),如users/1234,通过资源标识符能够标识并访问该资源。

除了单个的资源外,资源集合表示多个相同类型的资源,如users。在系统设计时,不同的实体之间往往存在某种关联关系,如一个用户有多个订单。同样,在REST中,这种关联关系也能够由资源之间的层次关系体现出来,如users/1234/orders/1。

由于REST以资源为中心,因此REST接口的端点(Endpoint)均以资源或资源集合结尾,它不像其他形式的Web服务一样以动词结尾,如api/GetUserInfo或api/UpdateUserInfo。在REST中,对资源的动作或操作是通过HTTP方法来完成的,如下:

GET http://api.domain.com/users/1234
PUT http://api.domain.com/users/1234

上例中用到了两个HTTP方法,分别为GET与PUT,它们的作用分别是获取和更新指定资源。当请求方发起请求,修改了资源的状态后,更新后的资源表述应返回给请求方,这也是表述性状态传递的意义。

从上面的例子可以看出,REST与HTTP有一定的关系,资源在服务的提供方与请求方之间进行传递,需要借助于协议来约定,比如协议所规定的消息格式等,而HTTP协议则是非常成熟且被广泛使用的网络协议。事实上,HTTP协议完全满足REST中所定义的约束,因此REST能够充分地使用HTTP协议以及其中的功能(如HTTP方法、HTTP消息等),并设计出松耦合的Web服务。1.1.3节将介绍REST约束,在1.2节中将会介绍HTTP协议。

REST定义了6个架构约束(Constraint),遵循这些约束的Web服务是真正的RESTful服务,即REST风格的服务。如果一个系统违反了其中的约束,则不能称其为RESTful,这些约束包括如下。

(1)客户端-服务器(Client-Server)

客户端-服务器约束体现了关注点分离(Separation of Concerns)原则,使客户端与服务端各自能够独立实现并独立开发,只要它们之间的接口不改变即可;客户端与服务端可以使用不同的技术或编程语言。

(2)统一接口(Uniform Interface)

统一接口是设计任何RESTful服务的基础,也是区别REST架构风格与其他Web服务风格的最主要约束。系统中的多个组件(包括服务端、客户端,以及可能存在的代理服务器等)都依赖于统一接口。统一接口约束本身又由4个子约束组成,分别如下。

前面提到过,任何能够命名的对象都是一个资源,资源能够通过统一资源标识符来区别。对于Web系统,统一资源标识符通常是一个URL,即统一资源定位符(Uniform Resource Locator)。每个URL代表一个资源或资源集合,当访问一个URL时,能够获取该资源或对它执行相应的操作。

当请求一个资源时,服务器返回该资源的一个表述。该表述表示资源当前的状态,它由表述正文和表述元数据组成,格式通常为JSON、XML和HTML等,比如以下代码是同一资源的两种不同的表述形式。

{
  "User": {
    "id": "123",
    "name": "Tom"
  }
}

<User>
    <id>1234</id>
    <name>Tom</name>
</User>

客户端在请求资源时,能够指定期望的表述格式,服务器在返回响应时,在响应中包含了指定表述格式的资源;访问同一个资源的不同格式无须修改资源的标识符,客户端也可以通过资源的表述(而非资源本身)对资源进行操作。

客户端与服务器之间传递的每一条消息都应包含足够的信息,这些信息不仅包含了资源的表述,也包含了资源表述的相关信息(如资源表述的格式与内容长度等),甚至包含了与该资源相关的其他操作信息。

服务器返回的资源表述中不仅要包含资源的表述,也应包含与之相关的链接,这些链接能够对资源执行其他操作,比如当获取资源时,返回的链接中包含更新该资源、删除该资源等链接。关于HATEOAS,第7章会有更详细的介绍。

(3)分层系统(Layered System)

分层系统约束能够使网络中介(如代理或网关等)透明地部署到客户端与服务器之间,只要它们遵循并且使用前面提到的统一接口约束即可;而客户端和服务端则都不知道网络中介的存在。中间服务器主要用于增强安全、负载均衡和响应缓存等目的。

(4)缓存(Cache)

缓存是Web架构中最重构的特性之一。客户端或网络中介均能够缓存服务器返回的响应,因此当服务器返回响应时,应指明该响应的缓存特性。对响应进行缓存将有助于减少数据获取延迟以及对服务器的请求,从而提高系统的性能。

(5)无状态(Stateless)

无状态约束将指明服务器不会记录或存储客户端的状态信息,反之,这些状态信息应由客户端来保存并维护,因此客户端对服务器的请求不能依赖于已发生过的其他请求,当客户端请求服务器时,必须在请求消息中包含所有与之相关的信息(如认证信息等)。

(6)按需编码(Code-On-Demand)

按需编码约束允许服务器临时向客户端返回可执行的程序代码(如脚本等),返回这些代码主要用于为客户端提供扩展性或自定义的功能。由于客户端必须理解并能够执行服务器返回的代码,因此这一约束增加了客户端与服务器之间的耦合,同时,这一约束是可选的。

理解REST及其约束,将有助于我们设计RESTful服务或RESTful API。然而在现实中,人们仍然对REST有错误的认识,认为只要有某种特性的API就是RESTful API。这些对REST错误的认识可能包含但不限于以下几种情况。

尽管REST风格的API同样具有上述特点并能够完成上述功能,然而这并不是说具有上述特点的API就是RESTful API。只有遵守了REST约束的API,才能够称为RESTful API。另外,Richardson成熟度模型是衡量API成熟度的一种方式,该模型进一步描述了各种Web API的特征,根据该模型,只有最成熟的API才是RESTful API。在7.4节中,将会更详细地介绍Richardson成熟度模型。

除了REST外,另一种常见的API风格是RPC风格,即远程过程调用(Remote Procedure Call)。下面是一个典型的RPC风格的API。

GET api.domain.com/getUserInfo

GET api.domain.com/UpdateUserInfo

REST风格与RPC风格的区别如下。

在几乎所有的情况中,REST是基于HTTP协议而实现的,因此深入了解HTTP协议是非常重要且必要的。对于Web开发人员而言,深入了解HTTP协议将有助于开发者开发出更好、更高质量的Web应用程序。此外,当应用程序出现问题时,也能够很容易地找出问题并解决问题。

超文本传输(Hyper Text Transfer Protocol,HTTP)协议,是互联网上应用最为广泛的一种网络协议,也是基于TCP/IP协议的应用层协议。其中最为常见的浏览网页的过程,就是通过HTTP协议来传递浏览器与服务器之间的请求与响应的,其流程图如图1-1所示。

图1-1 HTTP协议流程图

从图1-1中可以看出,HTTP协议采用了请求/响应模型。当客户端(通常是浏览器)发起一个HTTP请求时,它首先会建立起到HTTP服务器指定端口(HTTP协议默认使用80端口)的TCP连接,而HTTP服务器则负责在该端口监听来自客户端的请求。当TCP连接成功建立后,浏览器就会向HTTP服务器发送请求命令,如GET /index.html HTTP/1.1。一旦收到请求,服务器会根据请求向客户端返回响应,其响应内容通常包括一个状态行(如HTTP/1.1 200 OK)和若干个消息头,以及消息正文。消息正文则是资源、请求的文件、错误或者其他信息等。

HTTP协议采用的是明文传输数据,这种方式并不安全,因此网景公司(Netscape)在1994年设计了HTTPS协议,即超文本传输安全协议(Hypertext Transfer Protocol Secure),也被称为HTTP over TLS,HTTP over SSL,在第8章中将有关于HTTPS协议更详细的介绍。

统一资源定位符(Uniform Resource Locator),即通常所说的URL,代表网络上一个特定的资源。URL作为URI的子集,一个URL就是一个URI,用于标识并定位资源。

对于HTTP而言,当用户在浏览器中输入了一个URL后,意味着他想要获取或查看一些资源。在互联网上,有无穷尽的、各种格式的资源,包括图片、HTML页面、XML、视频、音频、可执行文件和Word文档等,通过URL才能在无数的资源中准确地定位或找到要查看的资源。每一个URL都代表一个不同的资源,因此,要访问HTTP资源,就需要使用URL。例如,当用户想要查看某个公司的网站首页时,就需要在浏览器中输入http://www.….com网址,如果要查看该公司的Logo,则需要输入http://www.….com/images/logo.png网址来获取代表此公司Logo的图片。浏览器会根据用户输入的URL向相应的服务器发送HTTP请求,而服务器会最终将对应的资源返回给客户端,并由浏览器处理后呈现。

对于一个URL,如http://www.….com/images/logo.png,它由以下几个部分组成。

除了上述3个主要部分外,URL还常常包括以下几个可选部分。

由此可见,一个完整的URL形式如下所示。

<protocol>://<host>[:port]/[path][?query][#fragment]

当HTTP服务器对请求返回响应时,它不仅返回资源本身,也会在响应中指明资源的内容类型(Content Type),也称媒体类型。要指定内容类型,HTTP依赖于MIME标准。MIME(Multipurpose Internet Mail Extensions),即多用途互联网邮件扩展类型,是一种表示文档的性质和格式的标准,因此,媒体类型也被称为MIME类型。MIME标准最初用于电子邮件,它用来告诉客户端具体是什么类型的内容。后来,HTTP协议也使用这一标准,并用作同样的目的。浏览器通过MIME类型来决定如何处理文档,因此服务器在返回响应时为资源设置正确的MIME类型非常重要。例如,对于音频、视频文件,只有设置了正确的MIME类型,才能被HTML语言中的<video>或<audio>所识别和播放。

当客户端请求HTML页面时,HTTP服务器会返回HTML内容,并标识其内容类型为text/html,前面text为主类型,后一部分html则是子类型。而当请求一个图片资源时,根据图片文件本身的格式,HTTP服务器将返回资源的媒体类型标记为image/jpeg或image/gif。因此MIME的组成结构非常简单,其语法为type/subtype,它由类型与子类型两个字符串构成,中间用“/”分隔,并且不允许空格存在。MIME类型对大小写不敏感,但是传统写法都是小写的,常见的MIME类型如表1-1所示。

表1-1 常见的MIME类型

类  型

描  述

典型示例

text

普通文本

text/plain、text/html、text/css、text/javascript

image

图片

image/gif、image/png、image/jpeg、image/bmp、image/webp

audio

音频

audio/midi、audio/mpeg、audio/webm、audio/ogg、audio/wav

video

视频

video/webm、video/ogg

application

二进制数据等

application/octet-stream、application/vnd.mspowerpoint、application/xml、application/pdf、application/json

其中比较常用的MIME类型及其意义如下。

前面已提到,HTTP是一个采用请求/响应模式的协议。客户端想要获取资源,就应向服务器发出请求,如果服务器能够正确处理来自客户端的请求,并且拥有客户端所请求的资源,它就能正确地响应,同时将资源返回给客户端。反之,如果客户端发出的请求有问题或者服务器上没有所要请求的资源,那么就无法返回客户端所期望的结果。

这个请求与响应过程如同“对话”一样,服务器与客户端都必须理解对方的“语言”,这正是HTTP消息所要解决的问题。当客户端向服务器发送请求时,应使用HTTP协议规定格式的消息;而服务器也会向客户端返回规定格式的响应,这样客户端才能够理解。HTTP消息正是服务器和客户端之间交换数据的方式,它有两种类型:请求消息和响应消息。

HTTP请求消息和响应消息具有相似的结构,它们都包括以下4部分的内容。

以下是典型的 HTTP 请求和响应的格式。

客户端请求如下:

GET / HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134
Accept-Language: zh-CN
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate
Host: microsoft.com
Connection: Keep-Alive

服务端响应如下:

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

其中,HTTP请求是由客户端发出的消息,用于请求服务器执行某个操作,它的起始行包括以下3项。

而HTTP响应的起始行被称作状态行,包含以下3项。

请求的起始行与响应的状态行,在上例中分别为GET / HTTP/1.1和HTTP/1.1 200 OK。

请求的最后一部分是它的正文。注意,并不是所有的请求都有正文,比如获取资源(GET)、获取资源元数据(HEAD),以及删除资源(DELETE)等请求,通常它们不需要正文,而那些要将数据从客户端发送到服务器的HTTP方法,它们若要创建资源或更新资源,就需要提供正文,比如POST和PUT等。

响应的最后一部分也是正文。与请求消息一样,不是所有的响应都有正文,状态码如 201或204等的响应,不包含正文。响应消息的正文通常是所请求资源的表述。

HTTP定义了一组请求方法,以表明要对指定资源执行的操作。每一个请求消息都必须包括一个HTTP方法,该方法将告诉服务器当前请求要执行哪一种操作。常见的HTTP方法有GET、POST、PUT、DELETE、PATCH、HEAD和OPTIONS等。

GET方法的作用是获取指定的资源,它并不会修改资源,因此GET方法是安全的。所谓安全方法,是指不会修改资源的方法。此外,GET方法也是幂等的。所谓幂等,是指多次对同一个URL调用同一个HTTP方法,其效果总是一样的。

POST方法的作用是创建资源。POST方法不是安全方法,因为它会修改服务器上的资源,并且也不是幂等方法,多次请求同一个POST操作会产生多个不同的资源。

PUT方法的作用是更新资源,因为PUT会修改资源,所以它也不是安全的方法。与POST方法不同的是,PUT方法是幂等的,多次更新同一个资源,其返回结果都是一样的。PUT方法除了更新资源外,当资源不存在时,它还可以创建资源。

需要注意的是,尽管POST与PUT方法都可以创建资源,但它们所请求的URI是有区别的,POST请求的URI是资源集合,而PUT则是请求单个不存在的资源,例如:

POST http://api.appdomain.com/users
PUT http://api.appdomain.com/users/1234

DELETE方法的作用是删除资源,它不是安全的,但它是幂等的,这意味着对同一资源请求多次DELETE方法,效果都是一样的。当第一次对资源调用 DELETE方法时,返回表示操作成功的200 OK状态码,后续再调用DELETE方法,由于资源已经不存在,则应返回404 Not Found状态码。

PATCH方法的作用是对资源进行部分更新,它与PUT方法的区别是:PUT会更新指定资源的全部内容,而PATCH可以根据需要仅更新资源的部分字段或属性。

HEAD方法与GET方法相同,但它并不返回消息正文,在响应消息中仅包含响应状态码与消息头,该方法常用来检测资源是否存在以及获取资源的元数据。

OPTIONS方法用于获取资源支持的操作,服务器在返回的响应中会包含Allow消息头,它的值为HTTP方法列表,例如:

Allow: GET, POST

综上所述,常见的HTTP方法总结如表1-2所示。

表1-2 HTTP方法总结

方法名称

作  用

安  全

幂  等

GET

获取资源

POST

创建资源

PUT

更新指定的资源

DELETE

删除指定的资源

PATCH

对资源进行部分更新

HEAD

与GET方法作用完全一样,但在响应中没有消息正文

OPTION

获取指定资源所支持的操作

客户端和服务器之间的请求消息与响应消息中均包含消息头,用来传递附加信息。一个消息头由消息头名称和它的值组成,中间用冒号“:”隔开,比如Content-Type: text/plain。

每个消息头都有特定的意义,比如上例的消息头用来指明请求或响应消息中正文的内容类型。HTTP请求与响应中均可包含多个消息头。

常见的请求消息头如表1-3所示。

表1-3 常见的请求消息头

消息头

说  明

示  例

Accept

可接受的响应内容类型(Content-Types)

Accept: text/plain

Accept-Charset

可接受的字符集

Accept-Charset: utf-8

Accept-Encoding

可接受的响应内容编码方式

Accept-Encoding: gzip, deflate

Accept-Language

可接受的响应内容语言列表

Accept-Language: en-US

Authorization

用于表示HTTP协议中需要认证资源的认证信息

Authorization: Basic OSdjJGRpbjpvcGVuIANlc2SdDE==

Cache-Control

用来指定当前请求中是否使用缓存

Cache-Control: no-cache

Connection

客户端(浏览器)想要优先使用的连接类型

Connection: keep-alive

Cookie

向服务器提供Cookie

Cookie: name=value; name2=value2

Content-Length

请求正文的长度

Content-Length: 348

Content-Type

请求正文的MIME类型(用于POST和PUT请求中)

Content-Type: application/json

Date

发送该消息的日期和时间

Date: Dec, 26 Dec 2015 17:30:00 GMT

Host

服务器的主机名以及使用的端口号

Host: www.itbilu.com:80

If-Match

仅当客户端提供的值与服务器上对应的值相匹配时,才进行对应的操作

If-Match: "9jd00cdj34pss9ejqiw39d82f20d0ikd"

If-Modified-Since

允许当请求资源未被修改时,返回304 Not Modified状态码

If-Modified-Since: Dec, 26 Dec 2015 17:30:00 GMT

If-None-Match

当服务器的任何资源和客户端提供的值不匹配时,服务器端才会返回所请求的资源

If-None-Match: "9jd00cdj34pss9ejqiw39d82f20d0ikd"

If-Unmodified-Since

仅当资源自某个特定时间以来未被修改时,才发送响应

If-Unmodified-Since: Dec, 26 Dec 2015 17:30:00 GMT

Origin

用于发起一个跨域资源共享的请求

Origin: http://www.domain.com

Proxy-Authorization

用于向代理进行认证的认证信息

Proxy-Authorization: Basic IOoDZRgDOi0vcGVuIHNlNidJi2==

User-Agent

浏览器的身份标识字符串

User-Agent: Mozilla/……

常见的响应消息头如表1-4所示。

表1-4 常见的响应消息头

消息头

说  明

示  例

Allow

用于指明资源支持的有效操作

Allow: GET, HEAD

Cache-Control

指明该响应使用的缓存机制

Cache-Control: max-age=3600

Connection

针对该连接所预期的选项

Connection: close

Content-Encoding

响应正文所使用的编码类型

Content-Encoding: gzip

Content-Language

响应正文所使用的语言

Content-Language: zh-cn

Content-Length

响应正文的长度

Content-Length: 348

Content-Type

响应正文的MIME类型

Content-Type: text/html; charset=utf-8

Date

消息被发送时的日期和时间

Date: Tue, 15 Nov 1994 08:12:31 GMT

ETag

表示资源的当前状态的一个标识符

ETag: "737060cd8c284d8af7ad3082f209582d"

Expires

指定一个时间,超过该时间则认为此响应已经过期

Expires: Thu, 01 Dec 1994 16:00:00 GMT

Last-Modified

所请求资源的最后修改日期

Last-Modified: Dec, 26 Dec 2015 17:30:00 GMT

Location

指向另一个URI,用于在进行重定向、成功创建新资源时使用

Location: https://localhost:5001/api/authors/1234

Proxy-Authenticate

要求在访问代理时提供身份认证信息

Proxy-Authenticate: Basic

Server

服务器的名称

Server: nginx/1.6.3

Set-Cookie

设置HTTP Cookie

Set-Cookie: UserID=itbilu; Max-Age=3600; Version=1

WWW-Authenticate

表示请求应使用的认证方式

WWW-Authenticate: Basic

除了标准的HTTP消息头外,一些Web应用程序还会添加自定义消息头,用于返回一些描述或备注类的信息。自定义消息头的名称一般以“X-”开头,以此来指明它并不是一个标准的HTTP消息头,例如X-AspNet-Version用于指明当前服务器运行的ASP.NET的版本。

HTTP响应状态代码由3个数字组成,用于指明HTTP请求的结果。在状态码后会有一个状态文本,它以文字形式简单描述状态的信息,如200 OK、404 Not Found和500 Internal Server Error等。根据其表述意义,状态码可分为以下5类。

状态码以其首位数字表示它所属的类别,而后两位则表示在该类别中具体的信息,表1-5为常见的HTTP状态码。

表1-5 常见的HTTP状态码

状态码

状态码名称

描  述

200

OK

请求操作成功执行,并且响应正文中包含预期的资源

201

Created

资源创建成功,响应正文为空

202

Accepted

已接受请求,并成功开始异步执行,但还未处理完成

204

No Content

请求的操作成功执行,响应正文为空

301

Moved Permanently

请求的资源已被永久移动,响应消息头中应包括资源的新URI,浏览器会自动重定向到新URI

303

See Other

对当前请求的响应可以在另一个URI上被找到,该URI在当前响应的Location消息头中

304

Not Modified

所请求的资源未修改,客户端可以从缓存中得到该资源;服务器返回此状态码时,消息正文不应包含任何内容(与204 No Content一样)

307

Temporary Redirect

服务端不处理客户端的请求,客户端应向另一个URI请求,该URI在当前响应的Location消息头中

400

Bad Request

客户端请求存在错误,如语法错误或请求参数有误,服务器无法理解

401

Unauthorized

当前请求要访问受保护资源,但却未向服务器提供正确的认证信息,或并未提供任何认证信息

403

Forbidden

当请求受保护资源时,尽管客户端提供了正确的认证信息,但由于权限不够,服务器禁止访问该资源

404

Not Found

请求的资源不存在

405

Method Not Allowed

请求的资源不支持客户端指定的HTTP请求方法,该响应的消息头中必须包含Allow项,用以表示当前资源能够接受的请求方法列表

406

Not Acceptable

服务器不支持请求中指定的资源表述格式(由Accept消息头指定)

409

Conflict

由于和被请求资源的当前状态之间存在冲突,请求无法完成,冲突通常发生于对PUT方法请求的处理

412

Precondition Failed

客户端请求头中指定了一个或多个先决条件,服务器验证这些先决条件失败

415

Unsupported Media Type

请求中使用了服务器不支持的资源表述格式(由Content-Type消息头指定)

500

Internal Server Error

服务器内部错误,无法完成请求

503

Service Unavailable

由于临时的服务器维护或者过载,服务器当前无法处理请求

REST作为一种架构风格,它不是标准,因此并没有一套确定的、公认的规则。尽管REST包含了6个用于指导设计出RESTful系统的约束,但在具体实现时,在很多细节上仍然会有多种多样的方式。不同的实现方式使系统具有不同的表现或不同的使用方式,因此在实现时,应遵循一些基本原则,也即最佳实践。

首先,在实现RESTful系统时,应正确地使用HTTP方法、HTTP消息头和HTTP状态码。

在上一节我们已经看到,HTTP协议对HTTP方法、消息头、响应码等都有详尽且明确的定义,当设计RESTful API时,应遵循其定义。比如,对于HTTP方法,应使用GET方法获取资源,使用POST方法创建资源。如果没有遵循这些方法的定义,则可能会出现使用POST方法获取、删除资源等情况,这是因为当客户端发送一个POST请求时,服务器上相应的方法不是创建资源,反而是获取或删除资源。

同样,客户端与服务端在进行请求与响应时,应正确地使用HTTP消息头。比如,当客户端要想指定资源的预期表述格式时,应使用Accept消息头,而非其他方式,这也正是该消息头的意义所在。

当服务器向客户端返回响应时,也应正确地使用HTTP状态码。比如,当操作成功却不需要返回响应正文时,应使用返回204 No Content(删除或更新资源)或201 Created(创建资源)。又如,当服务器不支持客户端指定的资源表述格式时,应返回406 Not Acceptable状态码。

由此可以看出,理解HTTP协议并正确地使用它对于设计出规范的RESTful API非常重要。除了这些原则以外,在设计资源的URI时也应注意下列原则。

PUT /users/1234/set-admin
DELETE /users/1234/set-admin
GET /users?role=admin
GET /users?searchQuery=abc
GET /users?pageSize=25&pageNumber=2

在RESTful API中,JSON和XML是最常用到的两种资源表述格式,它们都可以用来传递数据,且都具有简洁、自描述等特点。

JavaScript对象表示法(JavaScript Object Notation,JSON)是一种轻量级的数据交换格式。它采用完全独立于编程语言的文本格式来存储、表示数据,简洁和清晰的层次结构使它成为理想的数据交换格式,不仅易于人阅读和编写,也易于机器解析和生成。JSON的MIME类型为aplication/json。

JSON的语法非常简单,它的数据使用名称/值对来表示,名称/值对包括字段名称和它的值,中间用冒号隔开。其中字段名称应使用双引号表示,字段的值如果是字符串,也应使用双引号表示,如"firstName" : "John"。

JSON数据项的值的类型可以是下列类型。

JSON对象在大括号{}中书写,对象可以包含多个名称/值对。

{
  "id": "1234",
  "name": "Tom"
}

多个相同的数据项构成了JSON数组,该数组使用方括号[]表示。

{
  "users": [
    {
      "id": "1234",
      "name": "Tom"
    },
    {
      "id": "1235",
      "name": "Smith"
    }
  ]
}

XML是可扩展标记语言(eXtensible Markup Language),它与HTML语言很相似,包含标签、属性等元素,主要用于传输和存储数据。与HTML不同的是,XML具有非常严格的层次结构,且一个标签必须同时具有起始标签与结束标签,XML中允许创建自定义标签。

XML文档必须包含根元素,该元素是文档中其他元素的父元素。文档中的所有元素形成了一棵文档树,这棵树从根部开始,并扩展到树的最底端。

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
</note>

在XML中,每个标签除了必须有起始标签与结束标签外,标签与标签间还必须要正确嵌套,比如以下示例。

<root>
  <child>
    <subchild>.....</subchild>
  </child>
</root>

另外,标签名区分大小写,大小写不同的标签会被认为是不同的标签。标签允许包含一个或多个属性,每个属性的值必须使用引号。

<user id="1234">
    <name>Tom</name>
</user>

JSON与XML均有各自的特点。相比XML,JSON要更简洁,且更容易解析。JSON是面向数据的一种格式,因此可以很容易地转换为高级编程语言中的对象,JSON现在已经被广泛地使用在各种数据处理与交互场合下。然而JSON并不支持注释,且扩展性也不如XML。XML是面向文档的一种格式,它支持注释,允许创建自定义标签和属性。这些标签不仅清楚良好地描述了数据,而且还增加了文档的可扩展性,但同时这也增加了XML文档的大小。另外,在表示复杂的层次结构类型的数据时,XML要比JSON更适合。

当API发生了变化,比如资源表述内容有新增项(字段或属性)或系统添加了新资源类型时,应使用不同的版本来区别对API的更改,为RESTful API添加版本有以下4种方式。

第7章将会详细介绍如何为API添加版本功能。

整体来看,本章所有内容都属于理论。通过本章的介绍,我们认识了REST、REST约束和HTTP协议等重要概念,理解并掌握这些基本知识对于开发REST风格的Web应用程序是非常重要的。同时,本章也讨论了人们对REST可能存在的一些错误认识、REST的最佳实践以及RESTful应用中常用到的JSON与XML格式等,这些将帮助我们进一步认识REST并设计出更规范、更灵活的RESTful API应用。

第2章将会介绍.NET Core和ASP.NET Core,ASP.NET Core是微软推出的新一代Web应用开发框架,后续章节将介绍如何基于该平台来创建RESTful API应用。


相关图书

程序员的制胜技
程序员的制胜技
Dapr与.NET微服务实战
Dapr与.NET微服务实战
精通ASP.NET Core MVC 第7版
精通ASP.NET Core MVC 第7版
深入浅出 ASP.NET Core
深入浅出 ASP.NET Core
ASP.NET Core真机拆解
ASP.NET Core真机拆解
.NET性能优化
.NET性能优化

相关文章

相关课程