jersey与resteasy一样都是JAX-RS即Java API for RESTful Web Services标准的实现,spring-boot-starter-jersey提供了对Jersey RESTful Web服务框架的支持,能够让我们轻松的构建RESTful Web工程。
新建工程
<groupId>com.wl.jersey</groupId>
<artifactId>jersey</artifactId>
<version>1.0-SNAPSHOT</version>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wl.jersey</groupId>
<artifactId>jersey</artifactId>
<version>1.0-SNAPSHOT</version>
<name>jersey</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<MainClass>com.wl.jersey.Application</MainClass>
<spring-boot-version>1.5.7.RELEASE</spring-boot-version>
<commons-io-version>2.6</commons-io-version>
<slf4j-api-version>1.7.5</slf4j-api-version>
</properties>
<dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot-version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jersey -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
<version>1.5.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.25.1</version>
</dependency>
<!---日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api-version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>${spring-boot-version}</version>
</dependency>
</dependencies>
<!-- Package as an executable jar -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-version}</version>
<configuration>
<mainClass>${MainClass}</mainClass>
<layout>JAR</layout>
</configuration>
<!-- repackage 生成两个 jar.original -->
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 指定maven 打包java 版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<!-- maven 编译打包resource 和 java 目录下所有文件 maven默认资源路径是resources -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
<include>*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
<include>*.*</include>
</includes>
</resource>
</resources>
</build>
</project>
application.properties
server.port=10001
启动类
package com.wl.jersey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
/**
* Created by Administrator on 2019/3/21.
*/
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class //不使用数据库
},scanBasePackages = "com.wl")
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setWebEnvironment(true);
app.run(args);
logger.info("application init success");
}
}
配置类
package com.wl.jersey.config;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
import javax.ws.rs.ApplicationPath;
/**
* Created by Administrator on 2019/3/23.
*/
@Configuration
@ApplicationPath("/app")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
packages("com.wl");
//防止文件上传报错No injection source found for a parameter of type public
register(MultiPartFeature.class);
// register(HelloWorldResource.class);
}
}
ResourceConfig 是The resource configuration for configuring a web application(配置Web应用程序的资源配置)
packages("com.wl")是Adds array of package names which will be used to scan for components也就是会扫描指定包名下所有的JAX-RS组件
register(HelloWorldResource.class)是注册HelloWorldResource作为一个自定义的JAX-RS组件(Register a class of a custom JAX-RS component)
HelloWorldResource
package com.wl.jersey.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* Created by Administrator on 2019/3/23.
*/
@Consumes(MediaType.WILDCARD)
@Produces({MediaType.APPLICATION_JSON,MediaType.TEXT_PLAIN})
@Path("/hello")
public class HelloWorldResource {
@Path("/world")
@GET
public String helloWorld(){
return "hello world";
}
}
@Consumes注解表示能够接受的请求的MIME 类型(请求头的Content-Type类型),能够作用在方法上
@MediaType是对应Content-Type的枚举 WILDCARD表示所有类型
@Produces注解表示响应的MIME类型(响应头的Content-Type)
@Path注解表示请求路径
@GET注解表示请求方法。常见的有get post
启动应用访问http://localhost:10001/hello/world
JAX-RS简单使用
1.设置服务base URI ,设置服务base URI有三种方法
1.1使用@ApplicationPath注解。修改JerseyConfig 如下(在javax.ws.rs.core.Application子类上加此注解无效)
package com.wl.jersey.config;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
import javax.ws.rs.ApplicationPath;
/**
* Created by Administrator on 2019/3/23.
*/
@Configuration
@ApplicationPath("/app")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
packages("com.wl");
// register(HelloWorldResource.class);
}
}
1.2 使用application.properties配置文件加入spring.jersey.application-path=/app配置如下
server.port=10001
spring.jersey.application-path=/app
1.3 使用ServletRegistrationBean修改启动类如下
package com.wl.jersey;
import com.wl.jersey.config.JerseyConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.servlet.ServletProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
/**
* Created by Administrator on 2019/3/21.
*/
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class //不使用数据库
},scanBasePackages = "com.wl")
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setWebEnvironment(true);
app.run(args);
logger.info("application init success");
}
@Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(
new ServletContainer(), "/app/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS,
JerseyConfig.class.getName());
return registration;
}
}
启动应用 访问http://localhost:10001/app/hello/world
[email protected] 资源或方法的相对路径
若希望一个Java类能够处理REST请求,则这个类必须至少添加一个@Path("/")的annotation;对于方法,这个annotation是可选的,如果不添加,则继承类的定义
Path里的值可以是复杂表达式,例如@Path("{id}"),其中的{xxx}表示一个模板参数,模板参数是定义在@Path里的通配符,它以 { 开始,中间是一堆字母和数字的混合串(不能包含 / 字符),以} 结尾。
Path也支持正则表达式
下面是我的各种类型的path匹配例子
package com.wl.jersey.resource;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
/**
* Created by Administrator on 2019/3/23.
*/
@Path("widgets")
@Consumes(MediaType.WILDCARD)
@Produces({MediaType.APPLICATION_JSON,MediaType.TEXT_PLAIN})
public class WidgetsResource {
//匹配路径:localhost:10001/app/widgets/(任意整数)
@Path("{id}")
@GET
public String getWidget1(@PathParam("id")Integer id){
return id + "";
}
//匹配路径:localhost:10001/app/widgets/(任意整数)/(任意字符)
@Path("{id}/{name}")
@GET
public String getWidget2(@PathParam("id")Integer id,@PathParam("name") String name){
return id +"-" + name;
}
//匹配路径:localhost:10001/app/widgets/getWidget3/(任意字符)/p
@GET
@Path("/getWidget3/{var}/p")
public String getWidget3(@PathParam("var")String var){
return var;
}
//匹配路径:localhost:10001/app/widgets/(任意一个或两个0-9的数值)/regex 与getWidget2有重合的匹配路径(重合的路径会匹配到正则匹配的方法下)
//正则匹配
//正则匹配格式 {参数名:正则表达式} 这里var为参数名称 [0-9]{1,2}为正则表达式
@GET
@Path("{var:[0-9]{1,2}}/regex")
public String getWidget4(@PathParam("var")String var){
return var;
}
}
分别输入http://localhost:10001/app/widgets/12、http://localhost:10001/app/widgets/12/wl、http://localhost:10001/app/widgets/getWidget3/varhhh/p、http://localhost:10001/app/widgets/12/regex试试效果
3.请求方法注解
@GET, @PUT, @POST, @DELETE, @HEAD, @OPTIONS 方法可以处理的HTTP请求方法类型
4.参数绑定注解
@PathParam, @QueryParam, @HeaderParam, @CookieParam, @MatrixParam, @FormParam,@FormDataParam,@BeanParam
[email protected]如同上面关于路径匹配的例子PathParam是获取匹配路径的值
[email protected]是获取请求参数键值对的值
[email protected]是获取请求头的键值对的值
[email protected]是获取cookie键值对的值
[email protected]是获取form表单(application/x-www-form-urlencoded)的键值对的值
[email protected]获取表单(multipart/form-data)的键值对的值
[email protected]直接将form表单数据以及其他头信息绑定到对象上
下面是我的例子 用例参考https://documenter.getpostman.com/view/2537807/S17rvomr
package com.wl.jersey.resource;
import org.apache.commons.io.IOUtils;
import org.glassfish.jersey.media.multipart.FormDataParam;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.io.*;
/**
* Created by Administrator on 2019/3/23.
*/
@Consumes(MediaType.WILDCARD)
@Produces({MediaType.APPLICATION_JSON,MediaType.TEXT_PLAIN})
@Path("/param")
public class ParamResource {
//http://localhost:10001/app/param/1/wl
@Path("{id}/{name}")
@GET
public String pathParam(@PathParam("id") Integer id,@PathParam("name") String name){
return id + ":" + name;
}
//http://localhost:10001/app/param/queryParam?id=1&name=wl
@Path("/queryParam")
@GET
public String queryParam(@QueryParam("id")Integer id,@QueryParam("name")String name){
return id + ":" + name;
}
@Path("/headerParam")
@GET
public String headerParam(@HeaderParam("lan") String lan){
return lan;
}
//http://localhost:10001/app/param/cookieParam
@Path("/cookieParam")
@GET
public String cookieParam(@CookieParam("_ga")String ga, @Context HttpServletRequest request){
return ga;
}
@Path("/formParam")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String formParam(@FormParam("id") Integer id,@FormParam("name") String name){
return id + ":" + name;
}
/**
* 获取上传文件输入流 需要依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.0</version>
</dependency>
*/
@Path("/formDataParam")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String formDataParam(@FormDataParam("file")InputStream inputStream) throws IOException {
IOUtils.copy(inputStream,new FileOutputStream(new File("123.jpg")));
return new FileInputStream("123.jpg").available() + "";
}
@Path("beanParam")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String beanParam(@BeanParam UserDto userDto){
return userDto.getId() + ":" + userDto.getName();
}
//application/json 绑定参数最方便
@Path("applicationJson")
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String applicationJson(UserDto userDto){
return userDto.getId() + ":" + userDto.getName();
}
public static class UserDto{
@FormParam("id")
private Integer id;
@FormParam("name")
private String name;
@HeaderParam("head")
private String head;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
}
}
5.ExceptionMapper全局异常捕获 注意@Provider注解
package com.wl.jersey.interceptor;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
/**
* Created by Administrator on 2019/3/23.
*/
@Provider
public class CustomExceptionMapper implements ExceptionMapper<Throwable> {
@Override
public Response toResponse(Throwable exception) {
Integer code;
String msg;
if(exception instanceof NotFoundException){
//没有找到资源路径
code = 1;
msg = "404";
}else if(exception instanceof JsonParseException){
code = 2;
msg = "json转换异常";
}else if(exception instanceof UnrecognizedPropertyException){
code = 3;
msg = "";
}else{
code = 4;
msg = exception.getMessage();
}
return Response.status(Response.Status.OK).type(MediaType.APPLICATION_JSON).entity(Result.Builder.custom(null).code(code).msg(msg).build()).build();
}
public static class Result<T> {
private Integer code;
private String msg;
private T body;
public Integer getCode() {
return code;
}
private void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
private void setMsg(String msg) {
this.msg = msg;
}
public T getBody() {
return body;
}
private void setBody(T body) {
this.body = body;
}
public static class Builder<T> {
private Integer code;
private String msg;
private T body;
private Builder<T> body(T body) {
this.body = body;
return this;
}
public Builder<T> code(Integer code) {
this.code = code;
return this;
}
public Builder<T> msg(String msg) {
this.msg = msg;
return this;
}
public static <T> Builder<T> custom(T body) {
return new Builder<T>().body(body);
}
public Result<T> build() {
Result<T> result = new Result<>();
result.setBody(this.body);
result.setCode(this.code);
result.setMsg(this.msg);
return result;
}
}
}
}
UserResource
package com.wl.jersey.resource;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
/**
* Created by Administrator on 2019/3/23.
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.WILDCARD)
@Path(("/user"))
public class UserResource {
@Path("/saveUser")
@POST
@Consumes(MediaType.APPLICATION_JSON)
public UserDto saveUser(UserDto userDto){
throw new NotFoundException("");
// return userDto;
}
public static class UserDto{
private Integer id;
private String name;
private String head;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
}
}
6.ContextResovler实现自定义的json序列化与反序列化ObjectMapper对象(譬如是否允许未知字段,是否序列化为空的字段等)
package com.wl.jersey.interceptor;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
/**
* Created by Administrator on 2019/3/23.
*/
@Provider
public class JacksonConfig implements ContextResolver<ObjectMapper> {
private static ObjectMapper mapper = new ObjectMapper();
static{
//允许单引号
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES,true);
//允许内容中含有注释符号/* 或 //
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS,true);
//允许没有引号的属性名字
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,true);
//设置timeZone
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//序列化配置
//不包含null的属性
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS,true);
//自身引用时报错
mapper.configure(SerializationFeature.FAIL_ON_SELF_REFERENCES,true);
//反序列化配置
//不允许json含有类不包含的属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,true);
}
@Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
由于jackson-jaxrs-base包中定义了JsonMappingExceptionMapper、JsonParseExceptionMapper两个jackson解析异常捕获类导致上面的CustomExceptionMapper不能捕获这两个异常(不知道什么原因)下面我们自定义JsonMappingExceptionMapper、JsonParseExceptionMapper异常捕获类,覆盖默认的两个
package com.wl.jersey.interceptor;
import com.fasterxml.jackson.databind.JsonMappingException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
/**
* Created by Administrator on 2019/3/23.
*/
//@Provider
public class CustomJsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
@Override
public Response toResponse(JsonMappingException exception) {
return Response
.status(Response.Status.OK)
.type(MediaType.APPLICATION_JSON)
.entity(CustomExceptionMapper.Result.Builder.custom(null).code(1).msg(exception.getMessage()).build())
.build();
}
}
package com.wl.jersey.interceptor;
import com.fasterxml.jackson.databind.JsonMappingException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
/**
* Created by Administrator on 2019/3/23.
*/
//@Provider
public class CustomJsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
@Override
public Response toResponse(JsonMappingException exception) {
return Response
.status(Response.Status.OK)
.type(MediaType.APPLICATION_JSON)
.entity(CustomExceptionMapper.Result.Builder.custom(null).code(1).msg(exception.getMessage()).build())
.build();
}
}
注意两个类都没有加上@Provider注解 我们需要在JerseyConfig中手动的注册这两个类(不知道什么原因加上@Provider注解还是没法覆盖默认的异常捕获类,可能与优先级有关)
JerseyConfig修改如下
package com.wl.jersey.config;
import com.wl.jersey.interceptor.CustomExceptionMapper;
//import com.wl.jersey.interceptor.CustomJsonMappingExceptionMapper;
import com.wl.jersey.interceptor.CustomJsonMappingExceptionMapper;
import com.wl.jersey.interceptor.CustomJsonParseExceptionMapper;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
import javax.ws.rs.ApplicationPath;
/**
* Created by Administrator on 2019/3/23.
*/
@Configuration
@ApplicationPath("/app")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
packages("com.wl");
register(CustomJsonMappingExceptionMapper.class);
register(CustomJsonParseExceptionMapper.class);
//防止文件上传报错No injection source found for a parameter of type public
register(MultiPartFeature.class);
// register(HelloWorldResource.class);
}
}
下面我们将请求json对象改为不合法的
首先将Integer类型的id修改为字符串类型
增加一个多余的字段
7.ValidationExceptionMapper校验请求参数
jersey-bean-validation包中为我们提供了一个校验参数异常捕获类,要实现请求参数的校验非常的简单,与spring-mvc相似
首先引用
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
修改我们的UserResource如下
package com.wl.jersey.resource;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.Date;
/**
* Created by Administrator on 2019/3/23.
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.WILDCARD)
@Path(("/user"))
public class UserResource {
@Path("/saveUser")
@POST
@Consumes(MediaType.APPLICATION_JSON)
public UserDto saveUser(@Valid UserDto userDto){
// throw new NotFoundException("");
return userDto;
}
public static class UserDto{
private Integer id;
private String name;
@NotEmpty(message = "head can not be empty")
private String head;
private Date createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
}
我们将请求的参数head设置为空
ValidationExceptionMapper为我们返回了如上的数据(没有对应的错误信息)。
7.1实现自定义的ValidationExceptionMapper
package com.wl.jersey.interceptor;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
/**
* Created by Administrator on 2019/3/23.
*/
public class CustomValidationExceptionMapper implements ExceptionMapper<ValidationException> {
@Override
public Response toResponse(ValidationException exception) {
if (exception instanceof ConstraintViolationException) {
final ConstraintViolationException cve = (ConstraintViolationException) exception;
StringBuilder sb = new StringBuilder();
Integer code = 1;
for(ConstraintViolation cv : cve.getConstraintViolations()){
sb.append(cv.getMessage()).append(";");
}
sb = sb.deleteCharAt(sb.length() - 1);
return Response
.status(Response.Status.OK)
.type(MediaType.APPLICATION_JSON)
.entity(CustomExceptionMapper.Result.Builder.custom(null).code(code).msg(sb.toString()).build())
.build();
}else{
return Response
.status(Response.Status.OK)
.type(MediaType.APPLICATION_JSON)
.entity(CustomExceptionMapper.Result.Builder.custom(null).code(1).msg("参数校验不合格").build())
.build();
}
}
}
同理,没有加上provider注解。我们在JerseyConfig中手动注册
package com.wl.jersey.config;
import com.wl.jersey.interceptor.CustomExceptionMapper;
//import com.wl.jersey.interceptor.CustomJsonMappingExceptionMapper;
import com.wl.jersey.interceptor.CustomJsonMappingExceptionMapper;
import com.wl.jersey.interceptor.CustomJsonParseExceptionMapper;
import com.wl.jersey.interceptor.CustomValidationExceptionMapper;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
import javax.ws.rs.ApplicationPath;
/**
* Created by Administrator on 2019/3/23.
*/
@Configuration
@ApplicationPath("/app")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
packages("com.wl");
register(CustomJsonMappingExceptionMapper.class);
register(CustomJsonParseExceptionMapper.class);
register(CustomValidationExceptionMapper.class);
//防止文件上传报错No injection source found for a parameter of type public
register(MultiPartFeature.class);
// register(HelloWorldResource.class);
}
}
再次上一个请求
8.ContainerRequestFilter 与 ContainerResponseFilter
两者具有相似性,这里只介绍ContainerRequestFilter的使用
package com.wl.jersey.interceptor;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
/**
* Created by Administrator on 2019/3/23.
*/
@Provider
public class CustomContainerRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
}
}
上面是ContainerRequestFilter的简单实现类,所有的请求都会执行filter方法。如果我们要获取HttpServletRequest对象只需使用@Context注解
@Context
private HttpServletRequest request;
如果我们不想请求继续走下去,譬如说头信息里面缺少了必须的party_id要直接返回给客户端只需要requestContext.abortWith
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String partyId = requestContext.getHeaderString("party_id");
if(partyId== null || partyId.equals("")){
requestContext.abortWith(Response.status(Response.Status.OK).type(MediaType.APPLICATION_JSON).entity("partyId is null").build());
}
}
[email protected]注解 表示匹配到真实的资源之前先执行过滤器
[email protected]注解 上面的过滤器会匹配到所有的资源路径。如果我们只需要匹配特定的资源路径,我们就可以使用@NameBinding注解
新建一个注解AuthAnnotation
package com.wl.jersey.annotation;
import javax.ws.rs.NameBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by Administrator on 2019/3/23.
*/
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD}) //可以放在类上也可以放在方法上
public @interface AuthAnnotation {
}
修改我们的CustomContainerRequestFilter (在类上加上AuthAnnotaion注解)
package com.wl.jersey.interceptor;
import com.wl.jersey.annotation.AuthAnnotation;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.NameBinding;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
/**
* Created by Administrator on 2019/3/23.
*/
@Provider
@AuthAnnotation
public class CustomContainerRequestFilter implements ContainerRequestFilter {
@Context
private HttpServletRequest request;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String partyId = requestContext.getHeaderString("party_id");
if(partyId== null || partyId.equals("")){
requestContext.abortWith(Response.status(Response.Status.OK).type(MediaType.APPLICATION_JSON).entity("partyId is null").build());
}
}
}
在UserResource saveUser方法上加上AuthAnnotaion注解并新增一个getUser的资源不加AuthAnnotation注解
package com.wl.jersey.resource;
import com.wl.jersey.annotation.AuthAnnotation;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.Date;
/**
* Created by Administrator on 2019/3/23.
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.WILDCARD)
@Path(("/user"))
public class UserResource {
@Path("/saveUser")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@AuthAnnotation
public UserDto saveUser(@Valid UserDto userDto){
// throw new NotFoundException("");
return userDto;
}
@Path("/getUser")
@POST
@Consumes(MediaType.APPLICATION_JSON)
public UserDto getUser(UserDto userDto){
return userDto;
}
public static class UserDto{
private Integer id;
private String name;
@NotEmpty(message = "head can not be empty")
private String head;
private Date createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
}
分别访问http://localhost:10001/app/user/saveUser 、http://localhost:10001/app/user/getUser
请求
POST /app/user/saveUser HTTP/1.1
Host: localhost:10001
Content-Type: application/json
party_id:
cache-control: no-cache
Postman-Token: 73b8c125-1f7c-4913-9c5c-84b108200ca8
{"id":"12","name":"wl","head":"asd"}------WebKitFormBoundary7MA4YWxkTrZu0gW--
响应
partyId is null
请求
POST /app/user/getUser HTTP/1.1
Host: localhost:10001
Content-Type: application/json
party_id:
cache-control: no-cache
Postman-Token: 3c6a731f-de82-4be7-b7df-bb3059a82490
{"id":"12","name":"wl","head":"asd"}------WebKitFormBoundary7MA4YWxkTrZu0gW--
响应
{
"id": 12,
"name": "wl",
"head": "asd"
}
可见getUser没有经过过滤器而saveUser执行了过滤器的filter方法