Jersey 2.X 过滤器和拦截器 (五)

过滤器和拦截器可以在客户端和服务器端使用(本文主要介绍服务器端使用)。过滤器可以修改请求和响应,包括修改头、实体和其他请求/响应参数。拦截器主要用于修改实体输入流和输出流。例如,您可以使用拦截器对输出和输入实体流进行压缩和解压缩。

无论是使用filter还是interceptor,都需要注册为provider。

 

一、过滤器

1、过滤器类型

  入站 出站
服务器端 ContainerRequestFilter ContainerResponseFilter
客户端 ClientRequestFilter ClientResponseFilter

在服务器端,入站过滤器还分Pre-matching 和 post-matching:

Pre-matching:由@Pre-matching注解标识,是在启动请求匹配之前执行的请求过滤器,即可以修改请求的资源,直接影响匹配的方法。

post-matching:默认都是后匹配过滤器,即在匹配到了合适的资源方法后,该过滤器将会应用。

 

2、请求过滤器,实现ContainerRequestFilter接口

(1)post-matching

@Provider
public class AuthorizationRequestFilter implements ContainerRequestFilter {

	@Override
	public void filter(ContainerRequestContext request) throws IOException {
		String authorization = request.getHeaderString("authorization");
		if (authorization == null || "".equals(authorization) || !authorization.startsWith("Basic")
				|| !Auth.allow(new String(Base64.getDecoder().decode(authorization.split(" ")[1])))) {
			request.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity(new Result(401, "认证失败"))
					.type(MediaType.APPLICATION_JSON).build());
			return;
		}
	}

}

(2)Pre-matching

@PreMatching
@Provider
public class MethodRequestFilter implements ContainerRequestFilter{

	@Override
	public void filter(ContainerRequestContext requestContext) throws IOException {
		if(requestContext.getMethod().equals("PUT") || requestContext.getMethod().equals("DELETE"))
			requestContext.setMethod("POST");
		
	}

}

和post-matching的区别就是加上@Pre-matching注解,将在找到实际资源之前进行拦截,所以甚至可以通过调用setRequestUri()方法直接转发请求。

所以在请求过滤器中,Pre-matching 一定会比 post-matching 先执行。

3、响应过滤器,实现ContainerResponseFilter接口

@Provider
public class ResponseFilter implements ContainerResponseFilter{

	@Override
	public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
			throws IOException {
		if(responseContext.getStatus() == 401)
			responseContext.getHeaders().add("WWW-Authenticate", "Basic realm=\".\"");
	}

}

比如对认证没通过的响应进行拦截,要求客户端输入用户名密码进行认证。

所以响应过滤器都是在实际资源处理完成之后执行,即使实际资源没有执行,比如404错误,都会经过响应过滤器。(一定会执行)

响应过滤器的filter方法中有两个参数,request只读,修改无效。

 

 

二、拦截器

服务器和客户端使用相同的拦截器API:ReaderInterceptor 和 WriterInterceptor。

ReaderInterceptor拦截器实在消息体读之前执行,WriterInterceptor拦截器在消息体写入之前执行;它们的主要目的是包装将在消息体中读取和写入使用的实体流。

1、读拦截器

@Provider
public class StartLoggerInterceptor implements ReaderInterceptor {

	@Override
	public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
		//。。。。
		return context.proceed();
	}

}

一定要注意两点:

A、必须要调用proceed()方法,该方法在内部调用下一个拦截器。

B、只有有消息体时,才会执行对应的拦截器,如果在请求的资源中并没有获取任何消息体,即使客户端传递了,也不会执行;如果在拦截器中获取了消息体,则处理完后一定要设置消息体,否则不会执行下一个拦截器。

 

三、绑定机制

默认情况下,无论是手动注册到Application或Configuration中,还是通过注解@Provider,都是全局的配置。

Jersey提供名称绑定和动态绑定两种方式,给我们灵活的进行配置过滤器和拦截器。

1、名称绑定

使用注解@NameBinding,

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NameBinding {
}

该注解只能用在注解上,只需要我们根据实际需要自定义注解即可。

(1)自定义注解

@NameBinding
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {

}

可以在方法和类上使用。 

(2)过滤器配置

@Authorization
@Provider
public class AuthorizationRequestFilter implements ContainerRequestFilter {

	@Override
	public void filter(ContainerRequestContext request) throws IOException {
		String authorization = request.getHeaderString("authorization");
		System.out.println(authorization);
		if (authorization == null || "".equals(authorization) || !authorization.startsWith("Basic")
				|| !new String(Base64.getDecoder().decode(authorization.split(" ")[1])).equals("wzy:123456")) {
			request.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity(new Result(401, "认证失败"))
					.type(MediaType.APPLICATION_JSON).build());
			return;
		}
	}
}

使用自定义注解@Authorization进行名称绑定。 

(3)使用 

@Path("/rest")
@Produces("application/json")
@PerLookup
public class HelloRestController {

	@Authorization
	@POST
	@Path("/jump")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	public Response jump(@FormParam("name") String name){
		User user = new User(name,12);
		return Response.ok().entity(user).build();
	}

	
	@GET
	@Path("/get")
	@Produces("application/json")
	public MyJaxbBean getBean(@FormParam("name") String name){
		return new MyJaxbBean(name, 28);
	}
}

我们使用@Authorization对jump方法进行名称绑定,另外getBean方法没有,当请求时,只有jump方法才会要求验证用户。

 

2、动态绑定

需要实现动态特征接口javax.ws.rs.container.DynamicFeature,在运行期对匹配的方法进行绑定。

@Provider
public class AuthDymic implements DynamicFeature {

	@Override
	public void configure(ResourceInfo resourceInfo, FeatureContext context) {
		if (resourceInfo.getResourceMethod().isAnnotationPresent(POST.class)
				|| RestController.class.isAssignableFrom(resourceInfo.getResourceClass())) {
			context.register(AuthorizationFilter.class);
		}
	}

	static class AuthorizationFilter implements ContainerRequestFilter {

		@Override
		public void filter(ContainerRequestContext request) throws IOException {
			String authorization = request.getHeaderString("authorization");
			System.out.println("auth");
			if (authorization == null || "".equals(authorization) || !authorization.startsWith("Basic")
					|| !new String(Base64.getDecoder().decode(authorization.split(" ")[1])).equals("wzy:123456")) {
				request.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity(new Result(401, "认证失败"))
						.type(MediaType.APPLICATION_JSON).build());
				return;
			}
		}
	}
}

会对post方法以及RestController类中的方法进行过滤。

 

四、执行顺序和优先级

当我们定义了更多的过滤器和拦截器,并且希望他们执行都按照自己期望的顺序执行时,可以使用Jersey提供的@Priority注解。该注释接收一个具有优先级的整数参数。

在ContainerRequestFilter、WriterInterceptor、ReaderInterceptor中,将以数字的升序执行,比如@Priority(1)将会比@Priority(2)优先执行;

在ContainerResponseFilter中,将以相反的方向实行,比如@Priority(2)将会比@Priority(1)优先执行。

 

同时也可以使用javax.ws.rs.Priorities类,该类定义了一些标准化的优先级:

public final class Priorities {

    private Priorities() {
        // prevents construction
    }

    /**
     * Security authentication filter/interceptor priority.
     */
    public static final int AUTHENTICATION = 1000;
    /**
     * Security authorization filter/interceptor priority.
     */
    public static final int AUTHORIZATION = 2000;
    /**
     * Header decorator filter/interceptor priority.
     */
    public static final int HEADER_DECORATOR = 3000;
    /**
     * Message encoder or decoder filter/interceptor priority.
     */
    public static final int ENTITY_CODER = 4000;
    /**
     * User-level filter/interceptor priority.
     *
     * This value is also used as a default priority for application-supplied providers.
     */
    public static final int USER = 5000;
}

 

你可能感兴趣的:(Jersey)