核心内容摘要
SimpleFOC之ESP32(六)—— 双电机协同控制实战
深度解析 RESTful API 与 HTTP 协议RESTful API 深度解析RESTful API 是遵循RESTRepresentational State Transfer表述性状态转移架构风格设计的应用程序编程接口API是目前互联网领域最主流的分布式系统交互规范核心目标是通过统
简洁的规则实现客户端与服务器的解耦让系统具备高可扩展性、可维护性和跨平台兼容性。
REST 并非协议而是由计算机科学家Roy Fielding在2000年的博士论文中提出的架构设计原则RESTful API 则是这一原则在 HTTP 协议上的具体落地实现几乎所有现代Web服务如微信开放平台、阿里云API、GitHub API都采用RESTful风格设计。
REST 核心六大架构原则REST 架构的核心是六大约束条件只有满足这些条件的系统才能被称为真正的RESTful系统这也是设计RESTful API的根本准则
客户端-服务器Client-Server将用户界面客户端与数据存储服务器分离二者通过标准化接口通信。
优势客户端和服务器可独立开发、升级如前端换框架/端型后端改存储引擎互不影响服务器可专注于数据逻辑客户端专注于交互体验。
无状态Stateless服务器不保存任何客户端的会话状态每次请求必须包含服务器处理该请求所需的全部信息如身份认证、请求参数、资源标识。
关键客户端负责维护会话状态如将Token存在LocalStorage/Header服务器接收到请求后独立处理并返回响应处理完成后不保留任何与该请求相关的状态。
优势服务器可水平扩展新增节点无需同步会话请求可被任意服务器节点处理提升容错性调试和测试更简单请求可独立复现。
缓存Cacheable服务器的响应必须明确标记是否可被缓存如HTTP的Cache-Control、ETag、Last-Modified头客户端可缓存可重用的响应减少重复请求提升性能。
核心缓存的是资源的表述而非资源本身避免客户端频繁向服务器请求相同数据降低服务器负载和网络延迟。
统一接口Uniform Interface这是REST架构最核心的原则也是RESTful API设计的关键通过统一的接口规范简化系统交互实现客户端与服务器的解耦。
该原则又分为4个子约束1资源的标识Identification of Resources每个资源在请求中都有唯一的标识符URI客户端通过URI定位资源。
示例https://api.example.com/users/123唯一标识ID为123的用户资源https://api.example.com/articles/456唯一标识ID为456的文章资源。
2通过表述操作资源Manipulation of Resources Through Representations客户端通过服务器返回的资源表述如JSON、XML、HTML操作资源无需知道服务器端资源的存储方式。
示例服务器返回JSON格式的用户数据表述客户端修改该JSON后通过请求提交给服务器服务器根据表述更新实际资源。
3自描述消息Self-Descriptive Messages每个请求/响应消息都包含足够的信息让接收方能够理解如何处理该消息无需额外的上下文信息。
示例HTTP请求通过MethodGET/POST/PUT等说明操作类型通过Content-Typeapplication/json、application/xml说明消息体格式通过Accept说明期望的响应格式。
4超媒体作为应用状态的引擎HATEOASHypermedia as the Engine of Application State这是REST的终极原则也是最易被忽略的服务器返回的响应中包含超媒体链接客户端通过这些链接导航到其他相关资源无需提前知晓整个API的URI结构。
示例获取用户123的响应中包含该用户的文章、评论等资源的链接客户端只需点击/调用链接即可访问而非硬编码URI{ id: 123, name: 张三, email: zhangsanexample.com, _links: { self: {href: https://api.example.com/users/123}, articles: {href: https://api.example.com/users/123/articles}, comments: {href: https://api.example.com/users/123/comments} } }注意目前大部分所谓的“RESTful API”并未实现HATEOAS属于简化版RESTful但核心的统一接口规则仍需遵守。
分层系统Layered System系统由多个分层的组件构成每个组件只与直接的上层/下层组件交互隐藏底层的实现细节。
示例客户端只与负载均衡层交互负载均衡层将请求转发给应用服务器应用服务器再与数据库层交互客户端无需知道数据库的位置、类型等信息。
优势提升系统的可扩展性可新增缓存层、安全层降低组件间的耦合。
按需编码Code-on-Demand可选服务器可根据客户端请求动态发送可执行代码如JavaScript、Java Applet扩展客户端的功能这是唯一的可选约束。
示例前端页面通过服务器返回的JavaScript代码实现动态交互无需提前将所有逻辑写死在客户端。
注意该约束使用较少因为会增加客户端的复杂度违背“简洁性”原则。
RESTful API 与 HTTP 协议的深度结合RESTful API 基于HTTP/
1或HTTP/2实现完全复用HTTP的协议特性方法、状态码、请求/响应头无需自定义协议规则这也是其成为主流的重要原因。
核心映射关系如下
资源API的核心抽象REST的一切围绕资源Resource展开资源是服务器端可被访问、操作的任何实体如用户、文章、订单、商品等。
资源的表现形式资源本身是客观存在的客户端通过表述Representation获取/操作资源常见表述格式为JSON目前最主流轻量、易解析其次是XML、YAML极少使用HTML。
资源的分类单个资源如单个用户/users/123集合资源如所有用户/users关联资源如用户123的文章/users/123/articles。
URI 设计规范简洁、语义化、唯一URI统一资源标识符是资源的“地址”RESTful API的URI设计需遵循语义化、无动词、层级清晰的原则核心是用名词表示资源用HTTP方法表示操作。
核心设计规则用复数名词表示资源集合主流约定便于统一规则如/users用户集合、/articles文章集合单个资源通过ID区分/users/
/articles/456不包含操作动词如get、add、update、delete因为操作由HTTP方法承担反例/getUser/
/addArticle非RESTful层级清晰用/表示资源的从属关系如/users/123/articles用户123的文章、/articles/456/comments文章456的评论避免过长层级一般不超过3层超过则通过查询参数优化全部使用小写字母单词间用-分隔而非下划线如/blog-posts博客文章避免使用驼峰命名不包含文件后缀如.json、.xml资源表述格式由Content-Type/Accept头指定反例/users/
json用查询参数Query String实现过滤、排序、分页、筛选如过滤/users?gendermaleage18男性且18岁以上用户排序/articles?sortcreate_timeorderdesc按创建时间降序分页/articles?page1size10第1页每页10条筛选/articles?categorytech科技分类的文章。
优秀URI示例# 单个/集合资源 https://api.example.com/users # 所有用户集合 https://api.example.com/users/123 # ID为123的用户单个 # 关联资源 https://api.example.com/users/123/articles # 用户123的所有文章 https://api.example.com/articles/456/comments # 文章456的所有评论 # 带查询参数的资源 https://api.example.com/articles?page2size20categorytech # 科技分类文章第2页每页20条
HTTP 方法表示对资源的操作RESTful API 复用HTTP的标准方法表示对资源的CRUD创建、读取、更新、删除操作每个方法有明确的语义服务器根据方法执行对应的逻辑禁止滥用POST方法如用POST实现所有操作。
HTTP方法核心语义对资源的操作幂等性缓存性典型场景GET读取查Retrieve是是获取单个/集合资源、查询筛选资源POST创建增Create否否创建新资源集合下新增、提交非幂等操作如支付PUT全量更新改Update是否替换整个资源需提供资源完整信息、新增已知ID的资源PATCH增量更新改Update否可实现为是否更新资源的部分字段无需提供完整信息DELETE删除删Delete是否删除单个/集合资源关键概念说明幂等性Idempotent指多次执行相同的请求得到的结果与执行一次的结果完全一致服务器状态无额外变化。
幂等方法GET、PUT、DELETE例如多次GET/users/123结果相同多次PUT/users/123更新相同信息服务器状态不变多次DELETE/users/123执行一次后资源已删除后续执行无效果非幂等方法POST、PATCH例如多次POST/users会创建多个相同的用户多次PATCH/users/123修改不同字段服务器状态会持续变化。
意义幂等方法可安全地重试如网络中断后重发请求非幂等方法重试可能导致重复操作需做防重处理。
缓存性GET方法的响应可被客户端/中间件如Nginx缓存减少重复请求POST/PUT/PATCH/DELETE的响应一般不缓存。
HTTP方法使用示例# GET读取资源 GET https://api.example.com/users # 获取所有用户 GET https://api.example.com/users/123 # 获取ID为123的用户 GET https://api.example.com/users/123/articles # 获取用户123的所有文章 # POST创建资源向集合中新增服务器生成ID POST https://api.example.com/users # 创建新用户请求体传用户信息服务器返回新用户ID和资源 POST https://api.example.com/articles/456/comments # 为文章456创建新评论 # PUT全量更新需提供完整资源信息/创建已知ID的资源 PUT https://api.example.com/users/123 # 全量更新ID为123的用户请求体传完整的用户信息缺省字段会被置空/默认值 PUT https://api.example.com/products/789 # 若产品789不存在则创建该ID的产品适用于已知ID的场景 # PATCH增量更新仅传需要修改的字段 PATCH https://api.example.com/users/123 # 仅更新ID为123的用户的姓名和邮箱请求体{name: 李四, email: lisiexample.com} # DELETE删除资源 DELETE https://api.example.com/users/123 # 删除ID为123的用户 DELETE https://api.example.com/articles/456/comments/789 # 删除文章456的789号评论
HTTP 状态码表示响应结果的语义RESTful API 复用HTTP的标准状态码表示请求的处理结果客户端可通过状态码快速判断请求是否成功、失败原因禁止自定义状态码如用200表示所有结果在响应体中自定义code。
状态码分为5类核心使用的状态码如下按使用频率排序1xx信息性请求已接收正在处理几乎不用100 Continue客户端继续发送请求体仅用于大请求。
2xx成功请求已成功处理200 OK通用成功适用于GET/PUT/PATCH/DELETE的成功响应201 Created创建资源成功适用于POST的成功响应响应头需包含Location指向新创建资源的URI如Location: https://api.example.com/users/123204 No Content成功但无响应体适用于DELETE的成功响应或无需返回内容的更新操作。
3xx重定向需要客户端进一步操作极少用API一般直接返回目标资源301 Moved Permanently永久重定向302 Found临时重定向304 Not Modified资源未修改客户端使用缓存配合ETag/Last-Modified。
4xx客户端错误请求有误服务器无法处理最常用的错误类型400 Bad Request通用客户端错误如请求体格式错误、参数缺失/无效401 Unauthorized未认证客户端需先进行身份认证如未传Token、Token无效403 Forbidden已认证但无权限执行该操作如普通用户尝试删除管理员资源404 Not Found资源不存在如访问/users/999999号用户不存在405 Method Not Allowed请求方法不被允许如对/users执行PUT方法409 Conflict请求与服务器资源状态冲突如创建用户时用户名已存在415 Unsupported Media Type服务器不支持请求体的格式如请求体是XML但服务器只接受JSON422 Unprocessable Entity请求体格式正确但业务逻辑验证失败如密码长度不足。
5xx服务器错误服务器处理请求时发生错误500 Internal Server Error通用服务器错误如代码异常、数据库故障502 Bad Gateway网关/反向代理收到无效响应如应用服务器宕机503 Service Unavailable服务器暂时不可用如维护、过载可返回Retry-After头告知重试时间504 Gateway Timeout网关/反向代理超时。
状态码使用原则精准匹配语义不滥用200如客户端参数错误应返回400而非200响应体错误信息错误响应需包含详细的错误信息如错误码、错误描述便于客户端排查问题示例# 400 Bad Request参数缺失 { code: PARAM_MISSING, message: 请求参数缺失必传参数name, email, details: [name字段未传, email字段未传] } # 409 Conflict用户名已存在 { code: USER_NAME_DUPLICATE, message: 用户名已被注册请更换用户名, details: [用户名zhangsan已存在] }
HTTP 请求/响应头传递元信息RESTful API 充分利用HTTP的头字段传递非业务的元信息避免将这些信息放在请求体/URI中核心常用头字段如下请求头Content-Type指定请求体的格式必传如application/json; charsetutf-8JSON格式、application/x-www-form-urlencoded表单格式Accept指定客户端期望的响应体格式如application/json服务器优先返回JSONAuthorization身份认证信息如Bearer {Token}JWT认证、Basic {Base64(用户名:密码)}基础认证不推荐Cache-Control客户端的缓存要求如no-cache不使用缓存、max-age3600缓存1小时If-None-Match/If-Modified-Since缓存验证配合服务器的ETag/Last-Modified头使用实现协商缓存。
响应头Content-Type指定响应体的格式如application/json; charsetutf-8Location创建资源成功201时指向新资源的URIETag资源的唯一标识如哈希值资源修改后ETag会变化用于缓存验证Last-Modified资源的最后修改时间用于缓存验证Cache-Control服务器的缓存指令如public; max-age3600公网可缓存有效期1小时、no-store不缓存X-Rate-Limit-Limit/X-Rate-Limit-Remaining接口限流信息如每分钟最大请求数、剩余请求数。
RESTful API 进阶设计规范除了基础的HTTP映射规则实际开发中还需遵循一些进阶规范保证API的易用性、安全性和可维护性
版本控制API发布后不可避免需要迭代升级为了兼容老版本客户端必须做API版本控制禁止直接修改现有API的逻辑/字段。
主流版本控制方式推荐优先级从高到低URI中加入版本最直观使用最广泛如https://api.example.com/v1/usersv1版本、https://api.example.com/v2/usersv2版本请求头中加入版本如X-API-Version: 1优点是URI更简洁缺点是不直观域名中加入版本如https://v
api.example.com/users适用于版本差异极大的场景缺点是域名管理复杂。
版本号规则使用主版本号如v
v2小版本迭代如bug修复、字段新增不升级版本号仅当核心逻辑、字段结构发生不兼容变更时升级主版本号。
身份认证与授权RESTful API 是无状态的常用的认证方式如下推荐优先级从高到低JWTJSON Web Token最主流客户端登录后获取JWT后续请求将JWT放在Authorization: Bearer {JWT}头中服务器验证JWT的有效性无需存储会话OAuth
0适用于第三方授权场景如微信登录、GitHub授权分为授权码模式、密码模式等核心是通过令牌Token访问资源不泄露用户账号密码API Key适用于服务器间的调用如后端服务调用阿里云API客户端将API Key放在请求头如X-API-Key: {Key}或查询参数中服务器验证Key的有效性基础认证Basic Auth将用户名和密码做Base64编码后传递未加密仅适用于测试环境禁止生产环境使用。
授权认证通过后服务器根据用户的角色/权限判断是否允许执行操作如管理员可删除所有用户普通用户仅可操作自己的资源常用RBAC基于角色的访问控制模型。
限流与防刷为了防止API被恶意攻击、过载必须做接口限流Rate Limiting常用的限流策略基于IP的限流限制单个IP的请求频率如每分钟100次基于用户/Token的限流限制单个用户的请求频率如每分钟50次更精准基于接口的限流对核心接口单独限流如支付接口每分钟10次。
限流实现后服务器在响应头中返回限流信息并对超出限制的请求返回429 Too Many Requests状态码。
数据返回规范统一的响应格式成功/失败响应使用统一的结构便于客户端解析示例JSON格式# 成功响应单条资源 { code: 0, // 业务成功码0表示成功 message: success, data: { // 业务数据单个资源/对象 id: 123, name: 张三, email: zhangsanexample.com } } # 成功响应集合资源带分页 { code: 0, message: success, data: { list: [/* 资源列表 */], total: 100, // 总条数 page: 1, // 当前页码 size: 10 // 每页条数 } } # 失败响应 { code: PARAM_MISSING, // 业务错误码 message: 请求参数缺失, // 错误描述 details: [] // 详细错误信息可选 }返回必要的字段避免返回冗余字段如密码、令牌、敏感信息减少数据传输量空值处理对空值返回null或空集合如[]避免返回undefined保证客户端解析不报错时间格式统一使用UTC时间戳如1718985600000或ISO 8601格式如
T08:00:00Z避免使用本地时间如
16:00:00防止时区问题。
异常处理区分客户端错误和服务器错误客户端错误4xx返回详细的错误信息服务器错误5xx不返回具体的异常堆栈避免泄露服务器信息仅返回通用错误描述如“服务器内部错误请稍后重试”全局异常处理服务器端实现全局异常拦截器统一捕获异常并返回标准化的错误响应避免返回原生的异常信息防重处理对非幂等操作如POST、PATCH做防重处理如通过请求ID、唯一业务标识防止重复创建资源、重复支付。
文档化API 是客户端与服务器的交互契约必须提供清晰、完整、实时更新的API文档常用的API文档工具Swagger/OpenAPI最主流支持自动生成API文档基于代码注解支持在线调试如Swagger UIPostman支持手动创建API文档支持团队协作、测试APIFox国产工具整合了API文档、测试、调试、Mock功能全面。
文档需包含的信息接口URI、HTTP方法、请求参数头参数、路径参数、查询参数、请求体、响应状态码、响应体格式、示例请求/响应、认证方式、限流规则、版本信息。
RESTful API 的优势与适用场景核心优势简洁、易理解、易开发基于HTTP标准无需学习自定义协议开发人员可快速上手解耦客户端与服务器客户端只需关注URI和HTTP方法服务器只需关注资源的处理二者可独立迭代高可扩展性无状态设计让服务器可水平扩展分层系统让系统可新增缓存、负载均衡等组件跨平台、跨语言基于HTTP/JSON几乎所有编程语言、端型前端、移动端、后端服务都支持实现真正的跨平台交互可缓存充分利用HTTP缓存机制提升系统性能降低服务器负载易测试可通过浏览器、Postman等工具直接测试无需专用的测试工具。
适用场景RESTful API 是通用的API设计风格适用于绝大多数互联网/分布式系统场景尤其是前后端分离的Web应用前端通过API获取数据渲染页面移动端应用iOS/Android通过API与服务器交互微服务架构微服务之间通过API实现跨服务调用开放平台如微信开放平台、阿里云API向第三方提供服务物联网设备设备通过API向服务器上报数据、接收指令。
不适用场景RESTful API 并非万能的以下场景更适合使用其他协议/架构实时通信场景如聊天、直播、实时监控需使用WebSocket双向实时通信而非RESTful API单向请求-响应高并发、低延迟的金融交易场景部分场景可使用gRPC基于HTTP/
Protobuf序列化效率高、延迟低大数据传输场景如文件上传/下载可使用FTP/SFTP或基于HTTP的分片上传/下载RESTful的扩展服务内部的高频调用微服务内部的高频调用可使用gRPC/Thrift提升传输效率。
RESTful API 与其他API风格的对比
RESTful API vs RPC APIRPCRemote Procedure Call远程过程调用是传统的API风格如Dubbo、SOAP、XML-RPC核心是调用远程方法与RESTful API的核心区别维度RESTful APIRPC API核心思想操作资源名词为核心调用方法动词为核心接口设计URIHTTP方法语义化方法名参数如getUser(
协议依赖基于HTTP标准可基于TCP/HTTP多为自定义协议数据格式以JSON为主轻量多为XML/Protobuf部分格式较重可扩展性高无状态、分层较低耦合度高跨平台性强HTTP/JSON通用较弱部分RPC框架有语言限制文档化易基于OpenAPI/Swagger较复杂需专用文档工具
RESTful API vs gRPCgRPC是Google推出的高性能RPC框架基于HTTP/2和Protobuf与RESTful API的对比维度RESTful APIgRPC传输协议HTTP/
1/2HTTP/2强制序列化格式JSON/XML/YAMLProtobuf二进制效率高性能一般JSON序列化/反序列化有开销高Protobuf效率高HTTP/2多路复用易用性高基于HTTP标准易开发测试较低需学习Protobuf、gRPC框架跨平台性强无语言限制强官方支持多语言适用场景通用场景、开放平台微服务内部调用、高并发低延迟场景
RESTful API 设计常见误区滥用POST方法用POST实现所有CRUD操作如POST /getUser、POST /deleteUser违背HTTP方法的语义URI中包含动词如/getUser/
/updateArticle/456将操作写在URI中而非用HTTP方法表示状态码滥用用200表示所有结果在响应体中自定义code如{code: 400, message: 参数错误}失去HTTP状态码的语义违背无状态原则服务器保存客户端的会话状态如Session导致服务器无法水平扩展URI层级过深如/users/123/articles/456/comments/789/replies可通过查询参数优化返回敏感信息响应体中返回用户密码、令牌、数据库连接信息等敏感数据缺乏版本控制直接修改现有API的逻辑/字段导致老版本客户端兼容问题忽略HATEOAS虽然HATEOAS是可选的但部分场景可通过超媒体链接提升API的易用性避免客户端硬编码URI请求体/响应体格式不统一不同接口的请求体/响应体格式混乱增加客户端解析难度缺乏文档API无文档或文档过时导致开发人员无法快速了解接口用法。
七、
总结RESTful API 并非一套严格的标准而是基于REST原则和HTTP协议的最佳实践其核心是以资源为中心通过统一的HTTP接口实现资源的CRUD操作。
设计高质量的RESTful API关键是遵循语义化、标准化、简洁化的原则用名词URI标识资源用HTTP方法表示操作用HTTP状态码表示响应结果用头字段传递元信息保证无状态设计充分利用缓存机制做好版本控制、认证授权、限流防刷、文档化遵循统一的请求/响应格式返回必要的信息避免冗余和敏感数据。
RESTful API 凭借其简洁、解耦、跨平台的特性成为了互联网领域API设计的事实标准掌握RESTful API的设计原则是后端开发、前端开发、架构师的必备技能。