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
2.@Path 資源或方法的相對路徑
若希望一個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
4.1@PathParam如同上面關於路徑匹配的例子PathParam是獲取匹配路徑的值
4.2@QueryParam是獲取請求參數鍵值對的值
4.3@HeaderParam是獲取請求頭的鍵值對的值
4.4@CookieParam是獲取cookie鍵值對的值
4.5@MatrixParam
4.6@FormParam是獲取form表單(application/x-www-form-urlencoded)的鍵值對的值
4.7@FormDataParam獲取表單(multipart/form-data)的鍵值對的值
4.8@BeanParam直接將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());
}
}
8.1@PreMatching註解 表示匹配到真實的資源之前先執行過濾器
8.2@NameBinding註解 上面的過濾器會匹配到所有的資源路徑。如果我們只需要匹配特定的資源路徑,我們就可以使用@NameBinding註解
8.3@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方法