spring-boot-starter-jersey的使用

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/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

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方法

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章