RESTful API规范补充以及JAX-RS传递参数的方法

搜索的过滤与排序

  搜索在RESTful API里是用GET方法实现的,URI一般是某个对象,可以是单个也可以是列表。以微博搜索为例,如果不加任何条件,其URI一般是这样的:

/api/weibo
/api/weibo/6

  但往往还需要限制一下搜索条件,例如评论数大于某个数,或者含有什么关键字,如果是搜索微博列表,也可能加上对日期作升序或降序排序的要求。这些附加条件,是作为query param附加在http请求里的。例如:

  • GET /weibo?deleted=true - 得到所有标记为删除的微博
  • GET /weibo?sort=-create-date - 按照创建日期反序排序得到微博列表
  • GET /weibo?sort=comment-count,-create-date - 先按微博评论数再按创建日期反序排序得到微博列表
  • GET /weibo?keyword=xxx - 得到包含关键字xxx的微博列表
  • GET /weibo?keyword=xxx&deleted=true&sort=comment-count,-create-date - 搜索关键字包含xxx的标记为删除的微博,按照评论数再按创建日期反序排序得到微博列表

  确定了搜索对象,例如weibo,那么在服务器端我们就知道需要查找哪个表,或者需要连接哪几个表。where后面的条件则根据附上的参数动态选择。order by后面的属性则根据sort后面的参数添加,带-号的代表降序,很容易翻译成sql语言。

  我们还可以选择返回哪些属性,或者不返回哪些属性。例如:

  • GET /weibo?fields=id,subject - 得到微博列表,每个微博里的字段只有id和subject两个,其他的都排除了。
  • GET /weibo?fields-exclude=id,subject - 得到微博列表,每个微博里的id和subject被排除了,输出其他的字段。

  这些参数就是select后面的列信息。

  由于RESTful API的资源是面向对象的,我们往往是对某一个对象作查找,如果在查找里需要附带其他资源对象的信息,也可以在query param里添加,用embed字段作为key。例如:

  • GET /weibo?embed=owner.id,owner.name - 得到微博列表,每个微博里的信息里加入这个微博的所有者的id和name两个属性。

  这些属性也是跟在select后面的,只要参数名与数据库的表名能一一对应即可。由于存在嵌入的对象属性,在数据库查找上往往便需要多表连接。

  上一讲说的翻页的页数和每页的数据项数也是作为query param传给服务器的。

常用的查询

  上面说的对搜索的过滤和排序是通用的,根据传入的参数而动态构造sql语句。而对于某些特别常用的查询,例如新浪微博里的最热微博top10,则可以用专用的名字代替,例如:

  • GET /weibo/top10

不太能归类的操作

  http是面向资源的,RESTful API是基于http的,其操作也是针对某个资源对象进行的,操作的类型则用http方法表达,例如PUT,POST,PATCH,DELETE,GET。然而,有些操作不是那么容易归类为上述的http方法,例如给微博点赞。我们可以把点赞操作理解成修改微博对象,按照这个定义是应该用PUT或PATCH方法。但是一般意义上我们说的对微博的修改是指编辑微博的文本内容。因此,有必要把点赞这个操作独立出来,会更加方便,意义也更加清晰。例如:

  • PUT /weibo/12/like - 给某个微博点赞
  • DELETE /weibo/12/like - 删除某个微博的赞

常用的status code

  • 200 OK - 正常返回,用于GET, PUT, PATCH的这些正常操作。
  • 201 Created - 用于POST创建对象正常返回。
  • 204 No Content - 用于DELETE,成功操作但没啥返回的。
  • 304 Not Modified - 有Cache,表示没改过。
  • 400 Bad Request - 指请求里面有些参数不对。
  • 401 Unauthorized - 没有登录
  • 403 Forbidden - 登录了但是没有授权访问某个资源
  • 404 Not Found - 资源不存在
  • 405 Method Not Allowed - 登录了但是不允许做某个操作
  • 410 Gone - 表示资源不再提供了,用来做老版本提示用的
  • 415 Unsupported Media Type - 請求的Centent Type不對
  • 422 Unprocessable Entity - 一般用于validation校验
  • 429 Too Many Requests - 请求太快太多,达到限制

  http提供了很多的status code。但在RESTful API里常用的就这几个。我们可以根据业务需求对不同的request返回相匹配的状态码。方法就是上一讲提到的构造Response对象返回以及异常处理。

JAX-RS怎样接收客户端的参数

  RESTful API的核心是URI,HTTP方法以及数据传输格式,在第一讲详细讲过。http header也十分重要,在第二讲讲过。除此之外,服务器与客户端的交互往往需要传递一些参数,例如对象id,需要更新的整个对象以及上面提到的query param等。这些参数的传递都是通过注解实现的。下面介绍一下常用的参数注解。

PathParam
1
2
3
4
5
@PATCH
@Path("{id}/remove")
@Consumes({"application/json"})
@Produces({"application/json"})
public void remove(@PathParam("id") Long id,@Context HttpServletRequest request) {

  需要删除某条微博,此时URI里必定含有该微博的id信息,可以用@PathParam注解从URI里提取参数。

QueryParam
1
2
3
4
5
@GET
@Path("danger-unsolved")
@Consumes({"application/json"})
@Produces({"application/json;charset=UTF-8"})
public Response findUnSolved(@QueryParam("page") int page,@QueryParam("per-page") int perPage,@Context HttpServletResponse response) {

  翻页信息用query param传递,用@QueryParam注解可以提取。

FormParam
1
2
3
4
5
6
7
8
9
@POST
@Path("login")
@PermitAll
@Consumes({"application/x-www-form-urlencoded"})
public Response login(@Context HttpServletRequest request,
@Context HttpServletResponse response,
@FormParam("username") String username,
@FormParam("password") String password,
@FormParam("remember_me") String rememberMe) {

  用POST请求提交的表格,其表单内容可以用@FormParam获取。

MatrixParam
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Path("/books")
public class BookService {
@GET
@Path("{year}")
public Response getBooks(@PathParam("year") String year,
@MatrixParam("author") String author,
@MatrixParam("country") String country) {
return Response
.status(200)
.entity("getBooks is called, year : " + year
+ ", author : " + author + ", country : " + country)
.build();
}
}

  上述的request请求URI是/books/2011;author=mkyong;country=malaysia。@MatirxParam可以提取用;分隔的一组key/value对。

HeaderParam和CookieParam

  @HeaderParam和@CookieParam注解都是提取header或者cookie里某个key的value。像@HeaderParam(“Referer”)和@CookieParam(“customerId”)等,非常简单。

获取HttpServletRequest和HttpServletResponse

  JAX-RS是基于servlet实现的。其每个资源方法都可以提取相应请求的HttpServletRequest和HttpServletResponse对象。利用这两个对象,我们可以在资源方法内构造Response对象,也可以修改header和cookie。这两个对象都可以用@Context注解获取。上面的一些例子也有体现。

payload对象

  http request的payload对象在网络中是以json或xml的格式传输的,只要JAX-RS里能保证其反序列化,那么在参数列表里直接用相应的POJO对象类名和实体名表示即可。例如:

1
2
3
4
5
@PATCH
@Path("{id}/block")
@Consumes({"application/json"})
@Produces({"application/json"})
public MessageDTO block(@PathParam("id") Long id,@Context HttpServletRequest request,MessageDTO messageDTO) {

  上面的MessageDTO对象便是http request的payload对象。