Resultful架构
1.JAX-RS标准 2.0
JAX-RS是JAVA EE6引入的一个新技术。JAX-RS即JAVA APIfor RESTful Web services,是一个Java编程语言的应用程序接口,支持按照表述性状态转移(REST)架构风格创建Web服务。JAX-RS标准中定义了目标、非目标和元素等内容。
目标:
1) 基于POJO(Plain Ordinary Java Object):JAX-RS的API提供一组注解和相关的接口、类,并定义了POJO对象的生命周期和作用域,规定使用POJO来公布Web资源。
2) 以HTTP(超文本传输协议)为中心:JAX-RS采用HTTP协议,并提供清晰的HTTP和同意资源定位(URL)元素来映射相关的API类和注解。JAX-RS的API不但支持通用的HTTP使用模式,还对WebDAV和Atom等扩展协议提供灵活的支持。
3) 格式独立性:JAX-RS对传输数据的类型/格式的支持非常宽泛,允许在标准风格之上使用额外的数据类型。
4) 容器独立性:JAX-RS的应用可以部署在各种Servlet容器中,比如Tomcat/jetty,也可以部署在支持JAX-WS的容器中。
5) 内置于Java EE:JAX-RS是Java EE规范的一部分,它定义了在一个Java EE容器内的Web资源类的内部,如何使用Java EE的功能和组件。
非目标:
1) 对J2SE6.0之前版本的支持:JAX-RS中大量使用了注解,需要J2SE6.0以及更新的版本,因此不支持之前的版本。
2) 对服务的描述、注册和探测:JAX-RS没有定义也无需支持任何服务的描述,服务的注册和服务的探测。
3) HTTP协议栈:JAX-RS没有定义新的HTTP协议栈。承载JAX-RS应用的容器提供对HTTP的支持。
4) 数据类型/格式类:JAX-RS:JAX-RS没有定义处理实体内容的类,它将这一类型的类交由使用JAX-RS的应用中的类去实现。
元素:
1) 资源类:使用JAX-RS注解来实现相关Web资源的Java类。
2) 根资源类:使用@Path注解,提供资源类树的根资源及其子资源的访问。资源类分为根资源类和子资源类。
3) 请求方法标识符:使用运行期注解@HttpMethod,用来标识处理资源的HTTP请求方法。该方法将被资源类的相应方法处理。
4) 资源方法:资源类中定义的方法,使用了请求方法标志符,用来处理相关资源的请求。
5) 子资源标志符:资源类中定义的方法,用来定位相关资源的子资源。
6) 子资源方法:资源类中定义的方法,用来处理相关资源的子资源的请求。
7) Providers:一种JAX-RS扩展接口的实现类,扩展了JAX-S运行期的能力。
8) Filter:一种用于过滤请求和相应的Provider。
9) Entity Interceptor:一种用于处理拦截消息读写的Provider。
10) Invocation:一种用于配置发布HTTP请求的客户端API对象。
11) WebTarger:一种使用URI标识的Invocation容器的对象。
12) Link:一种携带与数据的URI,包括媒体类型、关系和标题等。
其他JAX-RS标准的实现
除了使用jersey实现了JAX-RS标准,还有其他项目也实现了该标准,比如RESTEasy和CXF项目。
其他REST实现
JAX-RS是Java领域实现REST式Web服务的标准规范,但不一定实现了REST式的Web服务同时也实现了JAX-RS标准。比如Restlet和Spring MVC项目在没有采用JAX-RS标准也实现了REST风格。
2.HTTP(超文本传输协议)
HTTP协议的请求主要由3部分组成:请求行、请求报头和请求体。其中某些请求报头和请求体的内容是可选的,请求报头和请求体之间需要用空行隔开。
2.1请求行
请求行只包含三个内容:方法、请求资源的URI和HTTP版本,格式如下:
Method Request-URI HTTP-Version CRLF
其中Method除了常用的CURD操作外还有如下几种:HEAD、TRACE、PATCH、MOVE、COPY、LINK、UNLINK、WRAPPED、Extension-method。
2.2请求报头
请求包头包含客户端传递请求的附加信息及客户端的自身信息。以下是常用的请求报头说明:
Accept:用于指定客户端所支持的信息类型,可重复指定。
Accpet-Charset:设置客户端可接收的字符集。
Accept-Encoding:设置文本压缩方法,值有gzip、compress和identity,优先级可以通过设置q(0-1)值的大小。
Accept-language:指定客户端可以接受的自然语言。
Host:指定被请求资源所在的主机和端口号,缺省端口号为80。
Connection:指定请求结束后是保持连接还是关闭链接。HTTP1.0下不支持该属性,HTTP1.1以上版本可以设置为keep-alive(默认)或者close。
2.3响应状态码
1xx:指示信息,表示请求被接收或正在处理;
2xx:表示请求成功;
3xx:表示重定向;
400:表示语义有误,该请求无法被服务器理解;
401:当请求需要用户验证的资源时,并被拒绝;
402:为可能的需求而预留;
403:服务器拒绝提供服务,主要是由于用户权限不足导致的错误;
404:最常见的错误,未找到相应资源;
415:请求的实体类型不匹配。
5xx:表示服务器错误。
3.REST API设计
3.1REST 统一接口
GET方法:
@GET
Public Response getStore(@QueryParam (“uuid”) String uuid){
……
}
PUT方法:
@PUT
@Path(“/{uuid}”)
Public Response updateStore(@PathParam(“uuid”) String uuid,
StoreInfoRequest request){
…….
}
POST方法:
@POST
Public Response createStore(StoreInfoRequest request){
…….
}
DELETE方法:
@DELETE
Public Response deleteStore(@QueryParam(“uuid”) String uuid){
…….
}
在JAX-RS标准中只定义了以上经常使用的几种方法以及@HEAD和@OPTIONS,相对于HTTP的方法来说,还有许多没有在JAX-RS中定义,不过可以通过WebDAV扩展方法对HTTP进行扩展,代码如下:
@Targer({ElementType.Method})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod(value = ”MOVE”)
@Documented
Public @interface MOVE{}
这段代码定义了MOVE注解,使用@HttpMethod注解定义了名为MOVE的HTTP扩展方法。有了这个扩展方法,就可以在资源类中定义新的方法来支持扩展方法的请求。实例如下:
@MOVE
Public Response MoveStore(@QueryParam(“uuid”) String uuid){
……
}
3.2资源定位
REST使用URI实现资源的定位,因此URI的设计对Web服务至关重要。其分解如下
Scheme://host:port/path?quertParam | |
Scheme | 指定协议名称,如HTTP、FTP等 |
Host | 服务器地址 |
Port | 服务提供的端口号 |
Path | 资源地址,通过‘/’符号体现层次结构 |
? | 分隔资源地址和查询参数 |
queryParam | 查询参数 |
& | 用来分隔查询条件的参数 |
, | 用来分隔有次序的参数 |
; | 用来分隔无次序的参数 |
@QueryParam注解:
该注解组成url的queryParam部分,可以通过它来定义查询参数。格式如下:
@QueryParam(“uuid”) String uuid;
@QueryParam(“access_time”) Date accessTime;
……..
@PathParam注解
该注解组成url的Path部分,它主要和@PATH注解成对使用。其中@PATH注解的使用格式:{参数名称:正则表达式}。具体格式如下:
@GET
@Path(“{start:\\d+}-{end:\\d+}”)
Public Response getStore(@PathParam(“start”) Long start,
@PathParam(“end”) Long end){
}
还有一种更加灵活的路径支持,即路径区间(PathSegment),可以和正则表达式生成更加宽泛的路径地址。具体如下:
@GET
@Path(“{region:.+}\store\{start:\\d+}-{end:\\d+}”)
Public Response getStore(@PathParam(“region”) List<PathSegment> ps,
@PathParam(“start”) Long start,
@PathParam(“end”) Long end)) {
}
该资源地址可以通过…/33/3301/store/2016-2017的形式访问。
@FormParam注解
该注解用于定义表单参数,相应的REST方法用以处理请求实体媒体类型为Content-Type:application/x-www-form-urlencoded的请求。示例代码如下:
@POST
Public Response updateUser(@FormParam(“account”) String account,
@FormParam(“pwd”) String pwd){
}
@Context注解
该注解用于解析上下文参数,JAX-RS中的多种元素都可以通过@Context注解作为上下文参数使用。示例代码如下:
Public Response getStore(@Context final Application application,
@Context final Request request,
@Context final UriInfo uriInfo,
@Context final Httpheaders headers){
}
3.3REST响应处理
返回类型:
1) void
在返回值类型是void的响应中,HTTP状态码为204。
2) Response
在返回值类型是Response的响应中,可以通过Response的entity方法定义实体类的实例。如果内容为空,返回的状态码也是204,正常是200。
3) GenericEntity
这部分内容和自定义类型类似。
4) 自定义类型
JDK中的类都可以作为返回值的类型,并且自己定义的实体类也可以作为返回值的类型。
处理异常:
JAX-RS的异常和HTTP状态码对应表 | |
HTTP状态码 | 异常类 |
400 | BadRequestExceotion |
401 | NotAuthorizedException |
403 | ForbiddenException |
404 | NotFoundException |
405 | NotAllowedException |
406 | NotAcceptableException |
415 | NotSupportedException |
3.4内容协商
@Produces注解
注解@Produces用于定义方法的响应实体的数据类型。可以定义一个或多个,同时可以为每种类型定义质量因素,质量因素取值范围从0~1之间。如果未定义质量因素,默认为1。案例如下:
@GET
@Produces(MedisType.APPLICATION_JSON_TYPE)
Public Response getStore(){
......
}
@Consumes
注解@Consumes用于定义方法的请求实体的数据类型。和@Produces注解功能相对,该注解的定义只用于JAX-RS匹配请求处理的方法,不做内容协商处理。如果匹配不到相应的类型,那么服务器会返回HTTP状态码415。示例代码如下:
@POST
@Consumes(“application/json;qs=.8”)
Public void createStore(){
......
}
4.REST 请求处理
4.1REST 过滤器
按照客户端向服务器进行请求操作时,该请求操作流程的先后顺序是:客户端请求过滤器->服务器请求过滤器->服务器响应过滤器->客户请求过滤器。这四种过滤器分别是:ClientRequestFilter、ContainerRequestFilter、ContainerResponseFilter以及ClientResponseFilter
ClientRequestFilter:
客户端请求过滤接口。定义了filter方法,该方法中带一个上下文类ClientRequestContext的参数。可以通过其实现类并覆盖fliter方法实现过滤操作。案例如下:
@Override
Public void filter(ClientRequestContext crc){
If(!crc.getHeaders().containsKey(“EVE-ACCESS-TOKEN”)){
……
}
}
ContainerRequestFilter:
服务器请求过滤接口。针对过滤切面,服务器请求过滤器接口ContainerRequestFilter的实现类可以定义为预处理和后处理。默认采用的是后处理方式,即先执行容器接收请求操作,接收并处理后在执行过滤。要更改过滤顺序可以通过在实现类上加@PreMarching注解改为预处理。案例如下:
@PreMatching
@Provider
@Service
@Priority(Priorities.AUTHENTICATION)
Public class RequestAuthenticationFilter implements ContainerRequestFilter{
@Override
Public void filter(ContainerRequestContext requestContext) throws IOExeption{
……
}
}
ContainerResponseFilter:
服务器响应过滤接口。定义的过滤方法filter()包含两个参数,除了前面的容器请求上下文类ContainerRequestContext,另一个是容器响应上下文类ContainerResponseContext。案例如下:
@Provider
Public class ResponseAdditionalHeaderFilter implements ContainerResponse{
@Override
Public void filter(ContainerRequestContext reqContext,ContainerResponseContext resContext) throws IOException{
resContext.getHeaders().putSingle(Pragma”,”no-cache”);
……
}
}
ClientResponseFilter:
客户端响应过滤器。定义的过滤方法filter()也包含两个参数,与容器上下文类类似。
4.2 REST 拦截器:
REST拦截器和过滤器都是在请求一响应模型进行切面处理。拦截器除了主要功能不同外,他的代码形式也不同。它没有客户端和服务端的区分,通常读写成对。JAX-RS定义的拦截器接口是ReadInterceptor和WriteInterceptor。在jersey也提供了一些拦截器类,比如DeflateEncoder、GZipEncoder和ContentEncoder类。
ReaderInterceptor:
读拦截器接口定义的拦截方法为aroundReadFrom(),该方法包含一个参数ReaderInterceptorContext,从中可以获得头信息、输入流和媒体类型等。
WriterInterceptor:
写拦截器接口定义的拦截方法是aroundWriteTo(),该方法也包含一个参数,即写拦截器上下文接口WriterInterceptorContext,从中可以获得头信息、输出流以及媒体类型等。
ContentEncoder类:
同时实现了读/写拦截器,并覆盖了方法。具体如下:
@Override
Public final Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException{
String contentEncoding = context.getHeaders()
.getFirst(HttpHeaders.CONTENT_ENCODING);
if (contentEncoding != null && getSupportedEncodings().contains(contentEncoding)) {
context.setInputStream(decode(contentEncoding, context.getInputStream()));
}
return context.proceed();
}’
4.3 异常处理 ExceptionMapper
通过实现ExceptionMapper接口并使用@Provider注解,可以实现通用的面向切面的异常处理。示例如下:
@Provider
Public classResponseExceptionmapper implements
ExceptionMapper<BaseResponseException>{
@Override
Public Response toResponse(BaseResponseException exception){
……
}
}
5. REST 客户端
Client接口
Client接口是REST客户端的基本接口,用于和REST服务器通信。Client被定义为一种重量级的对象,其内部要管理客户端通信底层实现所需的各种对象,比如连接器、解析器等。具体代码如下:
ClientConfig config = new ClientConfig();
config.register(new AnotherClientFilter());
Client client = ClientBuilder.newClient(config);
WebTarget接口
WebTarget接口是为REST客户端实现资源定位的接口。通过WebTarger接口,可以定义请求资源的具体地址、查询参数、媒体类型等。WebTarget可以链式设置属性,但每次返回的WebTarget都是新的对象,和StringBuffer类调用append()方法返回自身有一定区别。示例如下:
WebTarget webtarget = client.target(URI);
webtarget.path(“store”);
Webtarget.path(“single”);
webtarget.queryParam(uuid”,”1”);
Builder builder = webtarget.request(MediaType.APPLICATION_JSON_TYPE);
Invocation接口
Invocation接口是在完成资源定位配置后,向REST服务端发起请求的接口。Builder是Invocation的内部定义接口,并继承了SyncInvoker接口。SyncInvoker定义了Http标准的请求方法,如GET、PUT、POST、Delete、Trace等。示例如下:
Invocation.Builder builder = webTarget.request(MediaType.APPLICATION_JSON_TYPE);
Store store = builder.get(Store.class);