spring-boot-starter-jersey的使用

时间:2024-03-17 12:36:09

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

spring-boot-starter-jersey的使用

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

spring-boot-starter-jersey的使用

 [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/12http://localhost:10001/app/widgets/12/wlhttp://localhost:10001/app/widgets/getWidget3/varhhh/phttp://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]

[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;
        }
    }
}

spring-boot-starter-jersey的使用

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修改为字符串类型

spring-boot-starter-jersey的使用

 增加一个多余的字段

spring-boot-starter-jersey的使用

 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设置为空

spring-boot-starter-jersey的使用

 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);
    }


}

再次上一个请求

spring-boot-starter-jersey的使用

 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注解

[email protected]的使用

新建一个注解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方法

参考    https://www.cnblogs.com/pixy/p/4838268.html