如何实现优雅的 REST ful 服务

REST(英文:Representational State Transfer,简称REST)描述了一个架构样式的网络系统,比如 web 应用程序。它首次出现在 2000 年 Roy Fielding 的博士论文中,Roy Fielding是 HTTP 规范的主要编写者之一。在目前主流的三种Web服务交互方案中,REST相比于SOAP(Simple Object Access protocol,简单对象访问协议)以及XML-RPC更加简单明了,无论是对URL的处理还是对Payload的编码,REST都倾向于用更加简单轻量的方法设计和实现。值得注意的是REST并没有一个明确的标准,而更像是一种设计的风格。

REST提供了一组架构约束,当作为一个整体来应用时,强调组件交互的可伸缩性、接口的通用性、组件的独立部署、以及用来减少交互延迟、增强安全性、封装遗留系统的中间组件。

如何实现一个优雅的 Restful 服务

  • 资源识别
  • 确定资源粒度
  • 设计优雅的 URI
  • 选择合适的 HTTP Method 和 HTTP 状态码
  • 选择资源的表述( JSON / XML)

资源识别

​ 在一般情况下,对资源的识别通常都是REST服务设计的第一步。因为在REST服务中,我们提供的接口都是一系列资源,业务逻辑需要需要操作资源来完成。所以对资源的识别有以下几种方案。

​ 实际生产环境中,资源一般就是实体类名词。例如学生注册,资源就是学生。可以使用student作为资源名词。

  • 找到合适领域名词
  • 将资源组织为集合
  • 将资源合并为复合资源
  • 计算或者处理函数

确定资源粒度

资源粒度的确定,应该从两方面考虑,一个是服务端、两一个是客户端。不同的方向,对于资源粒度的划分有不同的考究。

服务端

  • 网络效率
  • 表述的多少
  • 客户端的易用程度

客户端

  • 可缓存性
  • 修改频率
  • 可变性

设计优雅的 URI

  • 使用域及子域对资源进行合理的分组和划分
  • 使用斜杠(/)来表示资源之间的层次关系
  • 在URI的路径部分使用逗号或者分号表示非层次关系
  • 使用连字符-或者下划线_改善路径名称可读性
  • 在URI的查询部分使用&字符来分隔参数
  • 在URI中避免出现文件扩展名(如.html,.jsp等)

HTTP方法

动作 安全 幂等 用途
GET Y Y 获取资源
POST N N 创建、更新资源
DELETE N Y 删除资源
PUT N Y 更新一个资源
HEAD Y Y 获取与GET相同的HTTP头信息
OPTIONS Y Y 获取资源支持的HTTP方法列表
TRACE Y Y 让服务器返回其受到的HTTP头

Restful方法示例

URI HTTP方法 意义
/user GET 获取所有用户信息
/user POST 新增新的用户信息
/user/{id} PUT 修改指定的用户信息
/user/{id} GET 获取指定的用户信息
/user/{id} DELETE 删除指定的用户信息

HTTP状态码

状态码 类型 意义
1XX 消息 这一类型的状态码,代表请求已被接受,需要继续处理。这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束。由于 HTTP/1.0 协议中没有定义任何 1xx 状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送 1xx 响应。
2XX 成功 这一类型的状态码,代表请求已成功被服务器接收、理解、并接受。
3XX 跳转、重定向、缓存相关 这类状态码代表需要客户端采取进一步的操作才能完成请求
4XX 客户端相关 这类的状态码代表了客户端看起来可能发生了错误,妨碍了服务器的处理。
5XX 服务端相关 类状态码代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。

各种http状态码枚举

CONTINUE(100, "Continue"),
SWITCHING_PROTOCOLS(101, "Switching Protocols"),
PROCESSING(102, "Processing"),
OK(200, "OK"),
CREATED(201, "Created"),
ACCEPTED(202, "Accepted"),
NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"),
NO_CONTENT(204, "No Content"),
RESET_CONTENT(205, "Reset Content"),
PARTIAL_CONTENT(206, "Partial Content"),
MULTI_STATUS(207, "Multi-Status"),
ALREADY_REPORTED(208, "Already Reported"),
IM_USED(226, "IM Used"),
MULTIPLE_CHOICES(300, "Multiple Choices"),
MOVED_PERMANENTLY(301, "Moved Permanently"),
FOUND(302, "Found"),
MOVED_TEMPORARILY(302, "Moved Temporarily"),
SEE_OTHER(303, "See Other"),
NOT_MODIFIED(304, "Not Modified"),
USE_PROXY(305, "Use Proxy"),
TEMPORARY_REDIRECT(307, "Temporary Redirect"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
PAYMENT_REQUIRED(402, "Payment Required"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
NOT_ACCEPTABLE(406, "Not Acceptable"),
PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"),
REQUEST_TIMEOUT(408, "Request Time-out"),
CONFLICT(409, "Conflict"),
GONE(410, "Gone"),
LENGTH_REQUIRED(411, "Length Required"),
PRECONDITION_FAILED(412, "Precondition Failed"),
REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"),
REQUEST_URI_TOO_LONG(414, "Request-URI Too Large"),
UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable"),
EXPECTATION_FAILED(417, "Expectation Failed"),
INSUFFICIENT_SPACE_ON_RESOURCE(419, "Insufficient Space On Resource"),
METHOD_FAILURE(420, "Method Failure"),
DESTINATION_LOCKED(421, "Destination Locked"),
UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"),
LOCKED(423, "Locked"),
FAILED_DEPENDENCY(424, "Failed Dependency"),
UPGRADE_REQUIRED(426, "Upgrade Required"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
NOT_IMPLEMENTED(501, "Not Implemented"),
BAD_GATEWAY(502, "Bad Gateway"),
SERVICE_UNAVAILABLE(503, "Service Unavailable"),
GATEWAY_TIMEOUT(504, "Gateway Time-out"),
HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported"),
VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"),
INSUFFICIENT_STORAGE(507, "Insufficient Storage"),
LOOP_DETECTED(508, "Loop Detected"),
NOT_EXTENDED(510, "Not Extended");