Restful API初学习
什么是RESTful API
RESTful API 是一种基于 REST(Representational State Transfer)架构风格设计的应用程序接口。它的概念包括:
- 资源: RESTful API 将一切事物都抽象为资源,比如一篇文章、一个用户、一个订单等。每个资源都有一个唯一的标识符(URI)来进行定位和访问,客户端通过操作这些 URI 来与资源进行交互。
- HTTP: 对资源的增删改查是通过HTTP 协议定义的 GET、POST、PUT、DELETE 等方法来对资源进行操作实现的,并使用HTTP状态码来表示操作结果。GET 用于获取资源信息,POST 用于创建新资源,PUT 用于更新资源,DELETE 用于删除资源。 这种基于 HTTP 方法的操作方式使得接口的语义清晰,易于理解和使用。
- 无状态: RESTful API 是无状态的,即服务器不会在不同请求之间记住客户端的状态信息。每个请求都是独立的,客户端需要在请求中包含所有必要的信息,服务器根据接收到的请求进行相应处理并返回结果。
- 数据表示: 资源可以以多种格式进行表示,如 JSON、XML 等。客户端和服务器可以协商使用哪种格式来进行数据的传输和交互,其中 JSON 由于其轻量级和易于解析的特点,在现代 RESTful API 中被广泛使用。
RESTful API 与传统 API 的区别:
- 传统 API没有统一固定的架构风格,可能基于不同的设计理念和技术实现
- 传统 API接口设计可能更多地基于具体的业务功能和操作,方法名和参数可能更加多样化和自定义,不一定严格遵循 HTTP 协议的语义,可能需要更多的文档和约定来解释接口的使用方式
- 传统 API接口数据传输格式可能更加多样化,除了 JSON 和 XML 外,还可能使用自定义的二进制格式或其他特定格式,在不同平台和语言之间的兼容性可能需要更多的处理和适配。
- 传统 API可能是有状态的,服务器需要在不同请求之间维护客户端的状态信息,这在某些情况下可能会增加系统的复杂性
RESTful API核心原则
REST 架构风格的六个核心原则:
客户端 - 服务器架构:将系统的功能划分为客户端和服务器两部分。客户端主要负责与用户进行交互,收集用户输入,展示数据等;服务器则负责存储和管理数据,处理客户端发送的请求,并将处理结果返回给客户端。这种架构使得客户端和服务器可以独立地进行开发、维护和升级,提高了系统的可扩展性和可维护性。同时,也便于根据不同的需求和场景,对客户端和服务器进行优化和部署。
无状态:服务器在处理每个请求时,不会依赖于之前请求的状态信息,即服务器不会在不同请求之间记住客户端的状态。无状态特性使得服务器更容易实现扩展,可以轻松应对大量的并发请求,也提高了系统的可靠性和故障恢复能力,当服务器出现故障时,更容易进行重启和恢复操作,而不会因为丢失状态信息而导致问题。
缓存:在客户端或服务器端对响应数据进行缓存。客户端可以缓存服务器返回的数据,当再次需要相同数据时,直接从缓存中获取,而不需要再次向服务器发送请求。服务器也可以设置缓存策略,告知客户端哪些数据可以缓存以及缓存的有效时间等信息。缓存能够显著提高系统的性能和响应速度,减少网络带宽的占用,降低服务器的负载。
分层系统:将系统划分为多个层次,每个层次都有特定的功能和职责,并且只能与相邻的层次进行交互。分层系统使得系统结构更加清晰,易于管理和维护。
统一接口:所有的资源都通过统一的接口进行访问和操作
- 每个资源都有一个唯一的标识符(URI),通过 URI 可以准确地定位和访问特定的资源
- 利用 HTTP 协议定义的 GET、POST、PUT、DELETE 等方法来对资源进行操作,使用HTTP状态码来表示操作结果。
- 请求和响应消息应该包含足够的信息,以便客户端和服务器能够理解消息的内容和含义
- 响应消息中可以包含指向其他资源的链接,客户端可以根据这些链接进行进一步的操作和导航,使得系统具有更好的灵活性和可发现性,用户可以通过链接方便地访问相关资源
统一接口使得 RESTful 系统具有更好的通用性和易用性
按需代码:服务器可以向客户端发送可执行代码,让客户端在本地执行这些代码,以扩展客户端的功能或实现特定的业务逻辑。按需代码可以在一定程度上增强客户端的功能,使得客户端能够根据服务器的要求动态地扩展或修改自身的行为,而不需要事先在客户端安装所有可能需要的功能模块。
GET/POST/PUT/DELETE
HTTP 方法是 RESTful API 的重要组成部分,是实现 RESTful 架构风格中统一接口的关键要素。
- GET:主要用于从服务器获取资源的信息,是一种安全且幂等的操作。安全意味着它只用于获取数据,不会对服务器上的资源产生修改、删除等副作用;幂等表示多次执行相同的 GET 请求,得到的结果应该是一致的,不会改变服务器资源的状态。
可以通过在 URL 中添加查询参数来实现更复杂的数据查询功能。比如,在一个商品管理系统中,客户端可以发送 GET 请求到/products?category=electronics&priceRange=100-500,获取价格在 100 到 500 之间的电子产品列表。
- POST:用于在服务器上创建新的资源,是非幂等的操作。客户端将包含新资源数据的请求发送给服务器,服务器根据请求中的数据在相应的资源集合中创建一个新的资源,并返回新创建资源的信息或状态。
除了创建资源,POST 还可以用于执行一些特定的操作,特别是那些不适合用 GET 来表示的操作,如提交表单、发起支付等。
- PUT:用于更新服务器上已存在的资源,要求客户端发送完整的资源数据。服务器会根据请求中的数据对指定 URI 的资源进行完全替换更新,是幂等的操作。
- DELETE:用于删除服务器上的资源,是幂等的操作。客户端向服务器发送 DELETE 请求,指定要删除的资源的 URI,服务器根据该 URI 删除相应的资源。
在删除主资源时,通常还会涉及到清理与该资源相关联的其他资源。比如在一个订单管理系统中,删除一个订单时,可能还需要删除与该订单相关的订单明细、库存占用记录等关联资源,这可以通过在服务器端的 DELETE 操作逻辑中进行处理。
- PATCH:用于对服务器上的资源进行部分更新,客户端只需在请求体中包含要更新的部分字段和数据。服务器会根据这些数据对指定资源进行部分修改,而不是完全替换,是非幂等的操作。
常见 HTTP 状态码在API中的应用
RESTful API中的HTTP状态码用于表示服务器对客户端请求的处理结果和状态,常见的状态码按类别可分为信息性状态码(1xx)、成功状态码(2xx)、重定向状态码(3xx)、客户端错误状态码(4xx)和服务器错误状态码(5xx),以下是一些常见HTTP状态码及其在API中的使用场景:
成功状态码(2xx)
- 200 OK
- 含义:表示请求已成功,服务器已成功处理了客户端的请求,并将请求的资源返回给客户端。
- 使用场景:在GET请求成功获取资源时,如客户端发送GET请求获取用户列表,服务器正常响应并返回用户列表数据,就会返回200状态码。
- 201 Created
- 含义:表示请求已成功,并且服务器创建了一个新的资源。
- 使用场景:通常在使用POST请求创建新资源时出现,比如客户端向服务器发送POST请求创建一个新的订单,服务器成功创建订单后,会返回201状态码,并在响应中包含新创建订单的相关信息。
- 204 No Content
- 含义:表示请求已成功,但服务器没有返回任何内容,通常用于只需要客户端执行某个操作,而不需要返回数据的情况。
- 使用场景:当客户端发送DELETE请求删除一个资源,服务器成功删除后,可能会返回204状态码,表示资源已成功删除,无需返回额外内容。
客户端错误状态码(4xx)
- 400 Bad Request
- 含义:表示客户端发送的请求有错误,服务器无法理解或处理该请求。
- 使用场景:当客户端发送的请求参数缺失、格式错误或不符合业务规则时,服务器会返回400状态码,比如客户端在创建用户时,没有提供必要的用户名参数,服务器就会返回400状态码,并在响应中说明错误原因。
- 401 Unauthorized
- 含义:表示客户端没有提供有效的身份验证信息,或者提供的身份验证信息无效,无法访问受保护的资源。
- 使用场景:当客户端尝试访问需要登录才能访问的资源,如获取用户个人信息,但没有提供有效的令牌或令牌已过期时,服务器会返回401状态码,提示客户端需要进行身份验证。
- 403 Forbidden
- 含义:表示客户端有身份验证信息,但没有足够的权限访问请求的资源。
- 使用场景:例如普通用户尝试访问管理员才能访问的系统设置页面,服务器会返回403状态码,告知客户端没有权限访问该资源。
- 404 Not Found
- 含义:表示客户端请求的资源在服务器上不存在。
- 使用场景:当客户端发送GET请求获取一个不存在的资源,如访问一个不存在的商品ID时,服务器会返回404状态码,表明找不到该资源。
服务器错误状态码(5xx)
- 500 Internal Server Error
- 含义:表示服务器在处理请求时发生了内部错误,通常是服务器端的程序出现了问题,如代码错误、数据库连接问题等。
- 使用场景:当服务器在执行某个操作时出现了未预料到的错误,无法完成客户端的请求,就会返回500状态码,例如服务器在处理订单时,由于数据库突然故障导致无法完成订单创建操作,服务器会返回500状态码。
- 503 Service Unavailable
- 含义:表示服务器暂时无法处理请求,通常是由于服务器过载、正在维护或其他原因导致服务不可用。
- 使用场景:当服务器由于进行系统升级、维护,或者由于流量过大导致无法正常处理请求时,会返回503状态码,告知客户端服务暂时不可用,请稍后再试。
RESTful API的URL设计最佳实践
- 基于资源设计:如
/users
表示用户资源集合;/users/{userId}
表示特定用户的资源,{userId}
是用户的唯一标识符 - 使用复数名词:有助于明确 URL 代表的是一个资源集合。
- 避免使用动词:URL 应该主要用于标识资源,而不是描述操作。
- 使用嵌套 URL 表示资源关系:如
/users/{userId}/orders
表示特定用户的订单资源集合 - 使用查询参数进行过滤和排序:/users?page=2&limit=10 表示获取第二页的用户信息,每页显示 10 条记录。
- 保持 URL 简洁和可读性,避免在 URL 中使用过长或复杂的路径和参数
- 版本控制:为了保证 API 的兼容性和可维护性,建议在 URL 或 HTTP请求中包含版本号
HATEOAS
HATEOAS 即 “Hypermedia as the Engine of Application State”,翻译为 “超媒体即应用状态引擎”,它是 REST(Representational State Transfer)架构风格的一个重要原则。简单来说,HATEOAS 允许服务器通过响应中的超媒体链接告知客户端当前可以执行的操作以及如何访问相关的资源,就像给客户端提供了一张 “地图”,让客户端能够根据当前的应用状态自主地决定下一步的行动。
传统的 API 通常需要客户端事先了解所有可能的操作和资源的 URL,这就要求客户端和服务器之间有紧密的耦合,并且需要详细的文档来描述 API 的使用方式。而 HATEOAS 使得 API 具有更强的自描述性,客户端可以通过服务器返回的超媒体链接了解当前资源支持的操作和可以访问的其他资源
示例:当客户端请求获取一个用户资源时,服务器的响应可能如下:
1 | { |
links 字段包含了与该用户资源相关的超媒体链接,客户端可以根据这些链接知道如何更新、删除该用户,以及如何访问该用户的订单资源,而不需要事先知道这些 URL。由于客户端是根据服务器返回的超媒体链接来进行操作的,当服务器端的资源结构或操作方式发生变化时,只要超媒体链接能够正确地反映这些变化,客户端就可以继续正常工作,不需要进行大规模的修改。这使得 API 的维护和扩展更加容易,降低了客户端和服务器之间的耦合度。
RESTful API的版本控制
API版本控制是RESTful API设计中的重要部分,它允许开发者在更新API时保持向后兼容性,同时不影响现有客户端的使用。以下是几种常见的API版本控制方法及其优缺点:
URL版本控制(URI Versioning)
- 方法:将版本号直接嵌入URL中。例如:
1
2https://api.example.com/v1/users
https://api.example.com/v2/users - 优点:
- 简单直观,易于理解和实现。
- 客户端可以明确指定所需的API版本。
- 缺点:
- URL变得冗长,不够优雅。
- 违反了REST的“资源唯一标识”原则,因为同一个资源可能有多个URL。
- 如果版本号变化频繁,维护成本较高。
自定义请求头版本控制(Header Versioning)
- 方法:通过自定义HTTP请求头传递版本信息。例如:
1
2
3GET /users HTTP/1.1
Host: api.example.com
Accept-Version: v2 - 优点:
- URL保持简洁,符合REST的设计原则。
- 版本信息与资源标识分离,更灵活。
- 缺点:
- 需要客户端显式设置请求头,增加了客户端的复杂性。
- 不易于通过浏览器直接测试。
Accept Header版本控制(Content Negotiation)
- 方法:利用HTTP的
Accept
头进行版本控制。例如:1
2
3GET /users HTTP/1.1
Host: api.example.com
Accept: application/vnd.example.v2+json - 优点:
- 完全符合HTTP协议的标准。
- URL保持简洁,版本信息与资源标识分离。
- 缺点:
- 客户端需要理解复杂的MIME类型。
- 不易于通过浏览器直接测试。
查询参数版本控制(Query Parameter Versioning)
- 方法:将版本号作为查询参数传递。例如:
1
https://api.example.com/users?version=v2
- 优点:
- 实现简单,易于测试。
- URL仍然保持一定的简洁性。
- 缺点:
- 查询参数通常用于过滤或排序,混用可能导致语义不清晰。
- 违反了REST的“资源唯一标识”原则。
域名版本控制(Domain Versioning)
- 方法:将版本号作为子域名或独立域名。例如:
1
2https://v1.api.example.com/users
https://v2.api.example.com/users - 优点:
- 完全隔离不同版本的API,适合大规模版本升级。
- URL清晰,易于管理。
- 缺点:
- 需要维护多个域名或子域名,增加了运维成本。
- 客户端需要知道具体的域名。
HATEOAS版本控制(动态版本控制)
- 方法:通过HATEOAS(超媒体作为应用状态引擎)动态提供API版本信息。客户端从初始入口点获取可用版本和资源链接。
- 优点:
- 完全符合REST的设计原则。
- 客户端无需硬编码版本信息,灵活性高。
- 缺点:
- 实现复杂,需要额外的元数据支持。
- 客户端需要支持HATEOAS。