java作为一门后端语言,其厉害之处在于web,大家比较熟知的各种网络应用,java都能做,那么在这个移动优先的时代,如何继续发挥java的强大呢。通常是让java作为一个app的服务端,为app客户端提供数据,做业务逻辑,所以我们用java来写接口,app客户端访问接口返回json文件进行解析,最后实现业务逻辑。这种方式就是我们通常所说的restful架构风格的api。
restful是一种架构思想,最初由Roy T. Fielding(HTTP/1.1协议专家组负责人)在其2000年的博士学位论文中提出。HTTP就是该架构风格的一个典型应用,其核心思想就是前后端分离,前端通过http请求,如www.xxxx.com/demo/username/password 来访问后端的接口,然后后端将处理好的数据封装为json返回,这样,后端只需关注具体逻辑 提供接口,而前端只关心界面,提高了程序解耦性。
在移动优先的时代,restful极为重要。通常一套后台可以让多种终端访问,包括移动端,pc端。
在java中比较容易实现restful的是SpringMVC框架,他提供了一套处理json的注解。通过@ResponseBody返回json数据,通过@ResquestBody解析json。
REST(Representational State Transfer)表象化状态转变(表述性状态转变),在2000年被提出,基于HTTP、URI、XML、JSON等标准和协议,支持轻量级、跨平台、跨语言的架构设计。是Web服务的一种新的架构风格(一种思想)
什么是轻量级:
代码不被侵入(正例:SpringMVC中不用接口和继承,仅用注解完成。反例:Struts中每一个Action都要继承核心控制器),轻量级跟包大小无关。耦合性越低,越轻量。
REST架构的主要原则
-
网络上每一个资源都有一个资源标志符,可以用来唯一地标识该资源
-
对资源的操作不会改变标识符。
-
同一资源有多种表现形式(xml、json)
-
所有操作都是无状态的(Stateless)
符合上述REST原则的架构方式称为 RESTful
RESTful资源操作
http方法 | 资源操作 | 幂等 | 安全 |
GET | SELECT | 是 | 是 |
POST | INSERT | 否 | 否 |
PUT | UPDATE | 是 | 否 |
DELETE | DELETE | 是 | 否 |
幂等性:对同一REST接口的多次访问,得到的资源状态是相同的。
安全性:对该REST接口访问,不会使服务器端资源的状态发生改变。
注意,默认情况下,PUT请求是无法提交表单数据的,在Spring MVC项目中需要在web.xml中添加过滤器解决:
<!-- 解决PUT请求无法提交表单数据的问题 --> <filter> <filter-name>HttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class> </filter> <filter-mapping> <filter-name>HttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
URL设计
- 动词的覆盖
有些客户端只能使用GET
和POST
两种方法。服务器必须接受POST
模拟其他三个方法(PUT
、PATCH
、DELETE
)。这时,客户端发出的 HTTP 请求,要加上X-HTTP-Method-Override
属性,告诉服务器应使用哪一个动词,覆盖POST
方法。
POST /api/Person/4 HTTP/1.1 X-HTTP-Method-Override: PUT //上面代码中,X-HTTP-Method-Override指定本次请求的方法是PUT,而不是POST。
- 宾语必须是名词
宾语就是 API 的 URL,是 HTTP 动词作用的对象。它应该是名词,不能是动词。比如,/articles
这个 URL 就是正确的,而下面的 URL 不是名词,所以都是错误的。
/getAllCars /createNewCar /deleteAllRedCars
- 复数 URL
既然 URL 是名词,那么应该使用复数,还是单数,没有统一的规定,但是常见的操作是读取一个集合,比如GET /articles
(读取所有文章),这里明显应该是复数。
为了统一起见,建议都使用复数 URL,比如GET /articles/2
要好于GET /article/2
。
- 避免多级 URL
常见的情况是,资源需要多级分类,因此很容易写出多级的 URL
//比如获取某个作者的某一类文章。这种 URL 不利于扩展,语义也不明确,往往要想一会,才能明白含义。 GET /authors/12/categories/ //更好的做法是,除了第一级,其他级别都用查询字符串表达。 GET /authors/12?categories=2 //下面是另一个例子,查询已发布的文章。你可能会设计成下面的 URL。 GET /articles/published //查询字符串的写法明显更好。 GET /articles?published=true
接口示例
//传统URL请求格式: http://127.0.0.1/user/query/1 GET 根据用户id查询用户数据 http://127.0.0.1/user/save POST 新增用户 http://127.0.0.1/user/update POST 修改用户信息 http://127.0.0.1/user/delete GET/POST 删除用户信息 //RESTful请求格式: http://127.0.0.1/user/1 GET 根据用户id查询用户数据 http://127.0.0.1/user POST 新增用户 http://127.0.0.1/user PUT 修改用户信息 http://127.0.0.1/user DELETE 删除用户信息
服务器回应
- 不要返回纯本文
API 返回的数据格式,不应该是纯文本,应该是一个 JSON 对象,这样才能返回标准的结构化数据。所以,服务器回应的 HTTP 头的Content-Type
属性要设为application/json
。
客户端请求时,也要明确告诉服务器,可以接受 JSON 格式,即请求的 HTTP 头的ACCEPT
属性也要设成application/json
。如:
GET /orders/2 HTTP/1.1
Accept: application/json
- 发生错误时,不要返回 200 状态码
//有一种不恰当的做法是,即使发生错误,也返回200状态码,把错误信息放在数据体里面,就像下面这样,解析数据体以后,才能得知操作失败。这张做法实际上取消了状态码,这是完全不可取的。 HTTP/1.1 200 OK Content-Type: application/json { "status": "failure", "data": { "error": "Expected at least two items in list." } } //正确的做法是,状态码反映发生的错误,具体的错误信息放在数据体里面返回。 HTTP/1.1 400 Bad Request Content-Type: application/json { "error": "Invalid payoad.", "detail": { "surname": "This field is required." } }
- 提供链接
API 的使用者未必知道,URL 是怎么设计的。一个解决方法是,在回应中,给出相关链接,便于下一步操作。这样的话,用户只要记住一个 URL,就可以发现其他的 URL,这种方法叫做 HATEOAS。
//举例来说,GitHub 的 API 都在 api.github.com 这个域名。访问它,就可以得到其他 URL。 { ... "feeds_url": "https://api.github.com/feeds", "followers_url": "https://api.github.com/user/followers", "following_url": "https://api.github.com/user/following{/target}", "gists_url": "https://api.github.com/gists{/gist_id}", "hub_url": "https://api.github.com/hub", ... }
//上面的回应中,挑一个 URL 访问,又可以得到别的 URL。对于用户来说,不需要记住 URL 设计,只要从 api.github.com 一步步查找就可以了。 //HATEOAS 的格式没有统一规定,上面例子中,GitHub 将它们与其他属性放在一起。更好的做法应该是,将相关链接与其他属性分开。 HTTP/1.1 200 OK Content-Type: application/json { "status": "In progress", "links": {[ { "rel":"cancel", "method": "delete", "href":"/api/status/12345" } , { "rel":"edit", "method": "put", "href":"/api/status/12345" } ]} }