WebService——JAX-RS2(Jersey)注解详解

JAX-RS2

JAX-RS2标准定义的元素:

  1. 资源类:使用JAX-RS注解来实现相关Web资源的Java类。
  2. 根资源类:使用@Path注解,提供资源类树的根资源以及子资源的访问。资源类分为根资源类和子资源类,由于Jersey默认提供WADL,每个应用公布的全部资源接口可以通过WADL页面查阅。
  3. 请求方法标识符:请求方法标识符:使用运行期注解@HttpMethod,用来标识处理资源的HTTP请求方法。该方法将使用资源类的相应方法处理,标准的方法包括DELETE、GET、HEAD、OPTIONS、POST、PUT。
  4. 资源方法:资源类中定义的方法使用了请求方法标识符,用来处理相关资源的请求。
  5. 子资源标识符:资源类中定义的方法,用来定位相关资源的子资源。
  6. 子资源方法:资源类中定义的方法,用来处理相关资源的子资源的请求。
  7. Provider:一种JAX-RS扩展接口的实现类,扩展了JAX-RS运行期的能力。
  8. Filter:一种用于过滤请求和响应的Provider。
  9. Entity Interceptor:一种用于处理拦截消息读写的Provider。
  10. Invocation:一种用于配置发布HTTP请求的客户端API对象。
  11. WebTarget:一种使用URI标识deInvocation容器对象。
  12. Link:一种携带元数据的URI,包括媒体类型、关系和标题等。

1、资源路径

1.1、@ApplicationPath

该注解用于声明Jersey的全局配置类,也即整个Jersey框架的程序入口。该类需要满足下面条件:

  • @ApplicationPath注解该类,并且指定该类对应的路径
  • 继承 org.glassfish.jersey.server.ResourceConfig 类
  • 在该类中配置Jersey的配置,例如声明资源包路径,配置拦截器等。
@ApplicationPath("/ws")
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        //注册单个资源类
        // register(DemoResource.class);
        //或直接声明资源包所在位置
        packages("pers.zhang.resource");
    }
}

1.2、@Path

JAX-RS2定义了@Path注解来定义根资源路径,@Path接收一个字符串value参数来解析资源路径地址。属性可以是文本字符、变量或变量外加一个定制的正则表达式。

使用方式:

  1. 静态:直接写路径
  2. 动态:配置@PathParam注解动态解析
@Path("hi")
public class DemoResource {

    @GET
    @Produces("application/json")
    public List<String> hi() {
        List<String> result = new ArrayList<>();
        result.add("hello world");
        result.add("hello Jersey");
        return result;
    }
}

2、请求方法

JAX-RS2的HTTP方法注解包括:

  • @GET
  • @PUT
  • @DELETE
  • @POST
  • WebDAV扩展方法

注意:JAX-RS2的HTTP方法注解可以定义在接口和POJO中,置于接口中的方法名更具抽象性和通用性。

2.1、@GET

HTTP的GET方法用于读取资源。GET方法是幂等的,因为读取同一个资源,总是得到相同的数据。GET方法也是安全的,因为读取资源不会对其状态做改动。JAX-RS2定义了@GET注解对资源方法定义,使得该方法用于处理GET请求。

@Path("book")//定义在接口
public interface BookResource {

    @GET//定义在接口
    public String getWeight();

}

public class BookResourceImpl implements BookResource {

    //实现类无需@GET注解
    @Override
    public String getWeight() {
        return "150M";
    }
}

//注意,加载的是实现类,而不是接口
register(BookResourceImpl.class);

测试:

@Test
public void testGet() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/book");
    Response response = webTarget.request().get();
    System.out.println(response.readEntity(String.class));
}

2.2、@PUT

PUT方法是一种写操作的HTTP请求。REST使用HTTP的PUT方法更新或添加资源。

PUT方法是幂等的,即多次插入或者更新同一份数据,
在服务器端对资源状态所产生的改变是相同的。PUT方法不是安全的,有写动作的HTTP方法都不是安全的。

@Path("book")//定义在接口
public interface BookResource {

    @PUT
    @Produces(MediaType.TEXT_PLAIN)
    @Consumes(MediaType.APPLICATION_XML)
    public String newBook(Book book);

}

public class BookResourceImpl implements BookResource {

    @Override
    public String newBook(Book book) {
        return book.toString();
    }
}

实体类:

@XmlRootElement
public class Book implements Serializable {

    private Long id;
    private String name;
    private Double price;

    public Book() {
    }

    public Book(Long id, String name, Double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    @XmlElement(name = "id")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @XmlElement(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlElement(name = "price")
    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

测试:

@Test
public void testPut() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/book");
    Book book = new Book(1001l, "xxxxx", 11.11);
    Entity<Book> bookEntity = Entity.entity(book, MediaType.APPLICATION_XML);
    Response response = webTarget.request(MediaType.TEXT_PLAIN_TYPE).put(bookEntity);
    System.out.println(response.readEntity(String.class));
}

在这里插入图片描述

2.3、@DELETE

DELETE方法是幂等的,即多次删除同一份数据(通常请求中传递的参数是数据的主键值),在服务器端产生的改变是相同的。JAX-RS2定义了@DELETE注解来定义相关资源方法。

@Path("book")//定义在接口
public interface BookResource {

    @DELETE
    public void delete(@QueryParam("bookId") long bookId);
}

public class BookResourceImpl implements BookResource {

    @Override
    public void delete(long bookId) {
        System.out.println(bookId);
    }
}
@Test
public void testDelete() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/book");
    Response response = webTarget.queryParam("bookId", "1001").request().delete();
    System.out.println(response.getStatus());
}

在这里插入图片描述

2.4、@POST

POST方法是一种写操作的HTTP请求。RPC的所有写操作均使用POST方法,而REST只使用HTTP的POST方法添加资源。

定义为POST的REST接口用于写数据,POST方法的特性是既不幂等也不安全。由于请求会改变服务器端资源的状态,因此它是不是安全的;由于每次请求对服务器端资源状态的改变并不是相同的,因此它不是幂等的。

@Path("book")//定义在接口
public interface BookResource {

    @POST
    @Produces(MediaType.APPLICATION_XML)
    @Consumes(MediaType.APPLICATION_XML)
    public Book createBook(Book book);
}

public class BookResourceImpl implements BookResource {

    @Override
    public Book createBook(Book book) {
        book.setId(System.currentTimeMillis());
        return book;
    }
}
@Test
public void testPost() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/book");
    Book book = new Book();
    book.setName("XXXXXX");
    book.setPrice(44.44);
    Entity<Book> bookEntity = Entity.entity(book, MediaType.APPLICATION_XML_TYPE);
    Response response = webTarget.request(MediaType.APPLICATION_XML_TYPE).post(bookEntity);
    System.out.println(response.readEntity(Book.class));
}
Book{id=1703407159663, name='XXXXXX', price=44.44}

在这里插入图片描述

2.5、WebDAV扩展方法

WebDAV(Web-based Distributed Authoring and Versioning,基于Web的分布式创作与版本控制)是IETF的RFC4918规范,是对HTTP1.1协议的一组扩展,该协议允许用户以协作方式编辑和管理远程Web服务器上的文件。WebDAV在HTTP方法的基础上,增加了如下方法:

  • PROPFIND方法:用于原子的更改和删除一个资源的多个属性。
  • PROPPATCH方法:用于原子地更改和删除一个资源的多个属性。
  • MKCOL方法:用于创建目录。
  • COPY方法:用于将资源从一个URI资源地址复制到另一个URI资源地址。
  • MOVE方法:用于将资源从一个URI资源地址 移动到另一个URI资源地址。
  • LOCK方法:用于锁定一个资源。WebDAV支持共享锁和独占锁。
  • UNLOCK方法:用于解锁一个资源。

虽然WebDAV对HTTP方法做出了功能性扩展,使之提供更强大服务,但是从ROA角度讲,因为WebDAV在HTTP标准方法的基础上增加了特殊的方法名称,WebDAV破坏了统一接口的原则。因此,对是否应该在REST式的Wb服务中支持WebDAV,业内的观点并不一致。

如果遵从ROA,那么就不使用HTTP标准方法之外的方法。如果业务需求确实超出了标准方法所及,那么可以使用如下注解实现对WebDAV的支持。JAX-RS2规范没有阐述对WebDAV提供支持的文字,但是JAX-RS2定义了@HttpMethod注解来定义相关的资源方法。在Jersey应用中,可以使用@HttpMethod注解定义HTTP标准方法之外的方法名称来支持WebDAV,示例代码如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod(value = "MOVE")
@Documented
public @interface MOVE {
}

3、资源定位(参数传递)

资源地址的路径变量是用来表达逻辑上的层次结构的,资源和子资源的形式是自左至右、斜杠分割的名词。它们的关系可以是从整体到局部,比如学校到班级,城市到乡镇;也可以是从一般到具体,比如一个生物的“门、纲、目、科、属、种”的资源路径。资源地址具体可以分为5个部分,以scheme:/host:port/path?queryString为例:

元素描述
scheme协议名称,通常是HTTP和HTTPS
host(DNS)主机名称或者IP地址
port服务端口
path资源地址,使用"/"符号来分割逻辑上的层次结构
?用来分割资源地址和查询字符串符号
queryString查询字符串,方法作用域信息
使用"&"符号分割查询条件
使用逗号分隔有次序的作用域信息
使用分号分隔无次序的作用域信息

在这里插入图片描述

通常使用ContextPath、ServletPath和PathInfo来细分资源地址。ContextPath是上下文名称,通常和部署服务器的配置或者REST服务的web.xml配置有关。ServletPath是Servlet的名称,与REST服务中定义的@ApplicationPath注解或者web.xml的配置有关。

JAX-RS2定义了@Path注解来定义资源地址。PathInfo是资源路径信息,与资源类、子类以及类中的方法定义的@Path注解有关。

3.1、@QueryParam

查询条件决定了方法的作用域,查询参数组成了查询条件。JAX-RS2定义了@QueryParam注解来定义查询参数。

/**
 * 对应:http://localhost:8080/ws/test?start=10&size=20
 */
@GET
public void test1(@QueryParam("start")int start, @QueryParam("size") int size) {
    System.out.println(start);
    System.out.println(size);
}

3.2、@DefaultValue

@DefaultValue的作用是预置一个默认值,当请求中不包含此参数时使用,示例如下:

/**
 * 对应:http://localhost:8080/ws/test?id=1
 * @DefaultValue指定默认值100
 */
@POST
public void test2(@DefaultValue("100") @QueryParam("id") int id) {
    System.out.println(id);
}

3.3、@PathParam

JAX-RS2定义了@PathParam注解来定义路径参数,每个参数对应一个子资源。

JAX-RS2定义了@Path注解来定义资源路径,@Path接收一个value参数来解析资源路径地址。该参数除了前面示例中的静态定义的方式外,也可以使用动态变量的方式,其格式为:{参数名:正则表达式}

1)、正则匹配
/**
 * from和to定义查询区间,\d+ 正则匹配数字,- 表示连接符
 * 匹配路径形如:http://localhost:8080/ws/test/111-999
 */
@GET
@Path("{from:\\d+}-{to:\\d+}")
public void test3(@PathParam("from")int from, @PathParam("to") int to) {
    System.out.println(from);
    System.out.println(to);
}

/**
 * 匹配路径形如:http://localhost:8080/ws/test/1,2000-12,2020
 */
@GET
@Path("{beginMonth:\\d+},{beginYear:\\d+}-{endMonth:\\d+},{endYear:\\d+}")
public void test4(@PathParam("beginMonth") int beginMonth, @PathParam("beginYear") int beginYear,
                  @PathParam("endMonth") int endMonth, @PathParam("endYear") int endYear) {
    System.out.println(beginMonth + "," + beginYear + "-" + endMonth + "," + endYear);
}
2)、路径配查询

查询参数和路径参数在一个接口中配合使用,可以更便捷地完成资源定位:

/**
 * 匹配路径形如:http://localhost:8080/ws/test/tom?hometown=ChangSha
 * return  tom:ChangSha
 */
@GET
@Path("{user:[a-zA-Z][a-zA-Z_0-9]*}")
public String getUserInfo(@PathParam("user") String user,
                          @DefaultValue("BeiJing") @QueryParam("hometown") String hometown) {
    return user + ":" + hometown;
}
3)、路径区间

路径区间(PathSegment)是对资源地址更灵活的支持,使资源类的一个方法可以支持更广泛的资源地址的请求。

/**
 * 匹配路径形如:http://localhost:8080/ws/test/Asia/China/northeast/liaoning/shenyang/tiexi
 * return Asia-China-northeast-liaoning-shenyang-tiexi
 */
@GET
@Path("{region:.+}/shenyang/{district:\\w+}")
public String getByAddress(@PathParam("region")List<PathSegment> region,
                           @PathParam("district") String district) {
    StringBuilder result = new StringBuilder();
    for (PathSegment pathSegment :region) {
        result.append(pathSegment.getPath()).append("-");
    }
    result.append("shenyang-" + district);
    return result.toString();
}

在这段代码中,getByAddress()方法用来匹配表的这些资源地址。该方法的region变量是PathSegment类型的数组,以匹配至少出现一个字符的正则表达式(+)。PathSegment如其名字所示,是路径的片段,是子资源的集合。遍历PathSegment集合,对于每一个PathSegment实例,可以通过调用其getPath()方法获取子资源名称。

/**
 * 匹配路径形如:http://localhost:8080/ws/test/q/restful;name=tom;age=18;height=177
 * restful是自定义前缀,可以用condition.getPath()获取。后续参数用 ; 分隔
 * return  name=[tom] age=[18] height=[177]
 */
@GET
@Path("q/{condition}")
public String getByCondition(@PathParam("condition") PathSegment condition) {

    StringBuilder conds = new StringBuilder();
    MultivaluedMap<String, String> matrixParameters = condition.getMatrixParameters();
    System.out.println(matrixParameters);
    Iterator<Map.Entry<String, List<String>>> it = matrixParameters.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<String, List<String>> entry = it.next();
        conds.append(entry.getKey()).append("=");
        conds.append(entry.getValue()).append(" ");
    }
    return conds.toString();
}

在这段代码中,getByCondition()方法只有一个PathSegment类型的参数condition,该参数包含了查询条件中携带的全部参数列表。举例来说,资源地址为http://localhost:8080/ws/test/q/restful;name=tom;age=18;height=177的请求可以匹配到getByCondition()方法,其中,
MultivaluedMap类型的实例matrixParameters的值为[name=[tom],age=[18],height=[177]]

3.4、@MatrixParam

@MatrixParam注解通过声明方式来获取参数。

/**
 * 匹配路径形如:http://localhost:8080/ws/test/q2/restful;name=tom;age=18;height=177
 * return    restful name=tom age=18 height=177
 */
@GET
@Path("q2/{condition}")
public String getByCondition2(@PathParam("condition") PathSegment condition,
                              @MatrixParam("name") String name,
                              @MatrixParam("age") Integer age,
                              @MatrixParam("height") Integer height) {
    return condition.getPath() + " name=" + name + " age=" + age + " height=" + height;
}

3.5、@FormParam

JAX-RS2定义了@FormParam注解来定义表单参数,相应的REST方法用以处理请求实体媒体类型为Content-Type:application/x-www-form-urlencoded的请求。

@Path("form")
public class FormResource {

    @POST
    public String newBook( @FormParam("name") String name,
                           @DefaultValue("22.22") @FormParam("price") Double price) {
        Book book = new Book(System.currentTimeMillis(), name, price);
        System.out.println(book);
        return book.toString();
    }
}

PostMan请求:
在这里插入图片描述

客户端测试:

@Test
public void testPostForm() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/form");
    Form form = new Form();
    form.param("name", "tom");
    form.param("price", "22.22");
    Response response = webTarget.request()
            .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
    System.out.println(response.readEntity(String.class));
}
Book{id=1703429235282, name='tom', price=22.22}

3.6、@Encoded

禁用对使用@QueryParam@PathParam@FormParam@MatrixParam绑定的参数值的自动解码。

  • 在方法上使用此注释将禁用所有参数的解码。
  • 在类上使用此注释将禁用所有方法的所有参数的解码。
@POST
public String newBook( @Encoded @FormParam("name") String name,
                       @DefaultValue("22.22") @FormParam("price") Double price) {
    Book book = new Book(System.currentTimeMillis(), name, price);
    System.out.println(book);
    return book.toString();
}

在这里插入图片描述

3.7、@BeanParam

JAX-RS2定义了@BeanParam注解用于自定义参数组合,使REST方法可以使用简洁的参数形式完成复杂的接口设计。

@GET
@Path("{region:.+}/shenyang/{district:\\w+}")
public String getByAddress(@BeanParam MyParam param) {
    System.out.println(param);
    return param.toString();
}
//自定义复杂参数Bean
public class MyParam {
    //将HTTP标头的值绑定到资源方法参数
    @HeaderParam("accept")
    private String accept;

    @PathParam("region")
    private String region;

    @PathParam("district")
    private String district;

    @QueryParam("station")
    private String station;

    @QueryParam("vehicle")
    private String vehicle;

    public String getAccept() {
        return accept;
    }

    public void setAccept(String accept) {
        this.accept = accept;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    public String getDistrict() {
        return district;
    }

    public void setDistrict(String district) {
        this.district = district;
    }

    public String getStation() {
        return station;
    }

    public void setStation(String station) {
        this.station = station;
    }

    public String getVehicle() {
        return vehicle;
    }

    public void setVehicle(String vehicle) {
        this.vehicle = vehicle;
    }

    @Override
    public String toString() {
        return "MyParam{" +
                "accept='" + accept + '\'' +
                ", region='" + region + '\'' +
                ", district='" + district + '\'' +
                ", station='" + station + '\'' +
                ", vehicle='" + vehicle + '\'' +
                '}';
    }
}

PostMan测试:
在这里插入图片描述

客户端测试:

@Test
public void testMyParam() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/form");

    Response response = webTarget.path("China").path("shenyang").path("tiexi")
            .queryParam("station", "Workers+Village").queryParam("vehicle", "bus")
            .request().get();
    System.out.println(response.readEntity(String.class));
}
MyParam{accept='text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2', region='China', district='tiexi', station='Workers+Village', vehicle='bus'}

3.8、@CookieParam

JAX-RS2定义了@CookieParam注解用以匹配Cookie中的键值对信息。

@GET
public String getHeaderParams(@CookieParam("longitude") String longitude,
                              @CookieParam("latitude") String latitude,
                              @CookieParam("population") double population,
                              @CookieParam("area") int area) {
    return longitude + "," + latitude + " population=" + population + ",area=" + area;
}

PostMan测试:
在这里插入图片描述

客户端测试:

@Test
public void testCookieParam() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/form");

    Response response = webTarget.request().cookie("longitude", "111.11")
            .cookie("latitude", "22.22")
            .cookie("population", "33.33")
            .cookie("area", "10")
            .get();
    System.out.println(response.readEntity(String.class));
}
111.11,22.22 population=33.33,area=10

3.9、@Context

JAX-RS2定义了@Context注解来解析上下文参数,JAX-RS2中有多种元素可以通过@Context注解作为上下文参数使用。

@POST
public void getContext(@Context Application application,
                         @Context Request request,
                         @Context Providers providers,
                         @Context UriInfo uriInfo,
                         @Context HttpHeaders headers) {
    MultivaluedMap<String, String> pathParameters = uriInfo.getPathParameters();
    MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters();
    List<PathSegment> pathSegments = uriInfo.getPathSegments();
    MultivaluedMap<String, String> requestHeaders = headers.getRequestHeaders();
    System.out.println(pathParameters);
    System.out.println(queryParameters);
    System.out.println(pathSegments);
    System.out.println(requestHeaders);
}
{}
{}
[form]
{user-agent=[PostmanRuntime/7.29.0], accept=[*/*], cache-control=[no-cache], postman-token=[2da6d4f7-7441-42bc-b38e-28416fbd5d1d], host=[127.0.0.1:8080], accept-encoding=[gzip, deflate, br], connection=[keep-alive], cookie=[area=10; latitude=22.22; longitude=111.11; population=33.33], content-length=[0]}

4、内容协商

客户端/浏览器通过使用HTTP AcceptAccept-CharsetAccept-LanguageAccept-Encoding头来定义接收头的信息,将其所期待的格式或MME类型告知服务器,服务器根据协商算法,返回客户端/浏览器可接受的数据信息。内容协商不只是数据格式协商,还包括语言、编码、字符集等信息。

  • Accept用于数据类型协商;
  • Accept-Language用于语言协商;
  • Accept–Charset用于字符集协商;
  • Accept-Encoding用于压缩算法协商。

JAX-RS2对内容协商的支持,是通过@Produces实现的,其他协商没有从架构上提供支持,可以通过编码从请求头中获取信息并处理。

4.1、@Produces

注解@Produces用于定义方法的响应实体的数据类型,可以定义一个多个,同时可以为每种类型定义质量因素(qualityfactor)。质量因素是取值范围从0到1的小数值。如果不定义质量因素,那么该类型的质量因素默认为1

1)、媒体类型不同的两个同等方法
@Path("conneg")
public class ConnegResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_XML)//响应媒体类型为XML
    public Book getJaxbBook(@PathParam("id") Long id) {
        return new Book(id, "xml", 22.22);
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)//响应媒体类型为JSON
    public Book getJsonBook(@PathParam("id") Long id) {
        return new Book(id, "json", 22.22);
    }
}

在这段代码中,getJaxbBook()getJsonBook()是同等质量因素、资源地址相同的两个GET方法,一个定义响应实体格式为XML,一个定义响应实体格式为JSON。

那么对同一个资源的访问,JAX-RS2该如何选择处理方法呢?

  • 如果请求中明确定义可接受的数据类型为两者之一,处理方法应该是定义相应数据类型的方法。
  • 如果两者都定义了,处理方法应该是质量因素高的方法。
  • 如果两者都定义,而且数据类型的质量因素是相等
    的或者没有定义Accept,XML的方法会被优先选择

没有定义Accept的情况,优先使用XML方法:
在这里插入图片描述

客户端明确表述格式的质量因素JSON高于XML,会选择JSON方法:

@Test
public void testConneg() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/conneg");

    Response response = webTarget.path("1")
            .request()
            .header("Accept", "application/xml;q=0.1,application/json;q=0.2")
            .get();
    System.out.println(response.readEntity(String.class));
}
{"id":1,"name":"json","price":22.22}
2)、一个方法中的多个媒体类型
@GET
@Path("book/{id}")
@Produces({"application/json; qs=.9", "application/xml; qs=.5"})
public Book getBook(@PathParam("id") Long id) {
    return new Book(id, "XXX", 22.22);
}

在这段代码中,getBook()方法定义了XML和JSON两种表述数据类型,XML的质量因素是0.5(0可以省略),JSON的是0.9。

如果客户端没有定义Accept,则使用服务端定义中质量因素更高的(JSON)媒体类型:

在这里插入图片描述

如果客户端请求中,明确接收的数据类型是两者之一,则响应实体使用客户端指定的类型:

@Test
public void testConneg2() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/conneg");

    Response response = webTarget.path("1")
            .request()
            .header("Accept", "application/xml")
            .get();
    System.out.println(response.readEntity(String.class));
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><book><id>1</id><name>xml</name><price>22.22</price></book>

如果客户端请求中,两者都定义但JSON的质量因素小于XML,则优先按照客户端的喜好选择响应实体的数据类型,即选择XML格式:

@Test
public void testConneg3() {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/conneg");

    Response response = webTarget.path("1")
            .request()
            .header("Accept", "application/xml;q=0.5,application/json;q=0.2")
            .get();
    System.out.println(response.readEntity(String.class));
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><book><id>1</id><name>xml</name><price>22.22</price></book>

4.2、@Consumers

注解@Consumes用于定义方法的请求实体的数据类型,和@Produces不同的是,@Consumes的数据类型的定义只用于JAX-RS2匹配请求处理的方法,不做内容协商使用

如果匹配不到,服务器会返回HTTP状态码415(Unsupported Media Type)

@POST
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Book getBook(Book book) {
    System.out.println(book);
    return book;
}

在这里插入图片描述

如果请求体为JSON格式,则返回HTTP状态码415:
在这里插入图片描述

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值