Jersey
1、传输格式
1.1、基本类型
Java的基本类型又叫原生类型,包括4种整型(byte、short、int、long)、2种浮点类型(foat、double)、Unicode编码的字符(char)和布尔类型(boolean)。
Jersey支持全部的基本类型,还支持与之相关的引用类型。
@Path("type")
public class TypeResource {
@POST
@Path("b")
public String postBytes(byte[] bs) {
for (byte b : bs) {
System.out.println(b);
}
return "byte[]:" + new String(bs);
}
}
客户端测试:
@Test
public void testBytes() {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/type");
byte[] bytes = "hello".getBytes();
Response response = webTarget.path("b")
.request()
.post(Entity.entity(bytes, MediaType.TEXT_PLAIN_TYPE));
System.out.println(response.readEntity(String.class));
}
byte[]:hello
1.2、文件类型
Jersey支持传输File类型的数据,以方便客户端直接传递File类实例给服务器端。文件类型的请求,默认使用的媒体类型是Content-Type:text/html。
@POST
@Path("f")
public File postFile(File f) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
String s;
while ((s = br.readLine()) != null) {
System.out.println(s);
}
return f;
}
}
PostMan测试:

客户端测试:
@Test
public void testFile() throws IOException {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/type");
URL resource = getClass().getClassLoader().getResource("application.yml");
File file = new File(resource.getFile());
Response response = webTarget.path("f")
.request()
.post(Entity.entity(file, MediaType.TEXT_PLAIN_TYPE));
File f = response.readEntity(File.class);
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
String s;
while ((s = br.readLine()) != null) {
System.out.println(s);
}
}
}
server:
port: 8080
1.3、InputStream类型
Jersey支持Java的两大读写模式,即字节流和字符流。
@POST
@Path("bio")
public String postStream(InputStream is) throws IOException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))){
StringBuilder result = new StringBuilder();
String s = null;
while ((s = br.readLine()) != null) {
result.append(s).append("\n");
System.out.println(s);
}
return result.toString();
}
}
PostMan测试:

客户端测试:
@Test
public void testStream() throws IOException {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/type");
URL resource = getClass().getClassLoader().getResource("application.yml");
File file = new File(resource.getFile());
Response response = webTarget.path("bio")
.request()
.post(Entity.entity(file, MediaType.TEXT_PLAIN_TYPE));
String result = response.readEntity(String.class);
System.out.println(result);
}
server:
port: 8080
1.4、Reader类型
以字符流作为REST方法参数。
@POST
@Path("cio")
public String postChar(Reader r) throws IOException {
try (BufferedReader br = new BufferedReader(r)) {
StringBuilder result = new StringBuilder();
String s = null;
while ((s = br.readLine()) != null) {
result.append(s).append("\n");
System.out.println(s);
}
return result.toString();
}
}
PostMan测试:

客户端测试:
@Test
public void testReader() throws IOException {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://127.0.0.1:8080/ws/type");
URL resource = getClass().getClassLoader().getResource("application.yml");
File file = new File(resource.getFile());
Response response = webTarget.path("cio")
.request()
.post(Entity.entity(file, MediaType.TEXT_PLAIN_TYPE));
String result = response.readEntity(String.class);
System.out.println(result);
}
server:
port: 8080
1.5、XML类型
XML类型是使用最广泛的数据类型。Jersey对XML类型的数据处理,支持Java领域的两大标准:
- JAXP(Java API for XML Processing,JSR-206)
- JAXB(Java Architecture for XML Binding,JSR-222 )
1)、JAXP标准
JAXP包含了DOM、SAX和StAX3种解析XML的技术标准:
- DOM是面向文档解析的技术,要求将XML数据全部加载到内存,映射为树和结点模型以实现解析。
- SAX是事件驱动的流解析技术,通过监听注册事件,触发回调方法以实现解析。
- StAX是拉式流解析技术,相对于SAX的事件驱动推送技术,拉式解析使得读取过程可以主动推进当前XML位置的指针而不是被动获得解析中的XML数据。
对应的,JAXP定义了3种标准类型的输入/输出接口:
- 输入:
- DOMSource
- SAXSource
- StreamSource
- 输出:
- DOMResult
- SAXResult
- StreamResult
DOM
支持输入输出类型分别为DOMSource和Document的请求。
@Path("xml")
public class XMLResource {
/**
* getDOMSource()和getDocument()方法使用DOM面相文档解析的技术,
* 支持输入输出类型分别为DOMSource和Document的请求
*/
@POST
@Path("dom")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public DOMSource getDOMSource(javax.xml.transform.dom.DOMSource domSource) {
//需要手动解析xml
return domSource;
}
@POST
@Path("doc")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Document getDocument(org.w3c.dom.Document document) {
//需要手动解析xml
Node person = document.getElementsByTagName("person").item(0);
Node id = document.getElementsByTagName("id").item(0);
Node name = document.getElementsByTagName("name").item(0);
Node age = document.getElementsByTagName("age").item(0);
System.out.println(id.getTextContent());
System.out.println(name.getTextContent());
System.out.println(age.getTextContent());
return document;
}
}

SAX
支持输入输出类型为SAXSource的请求。
/**
* getSAXSource()方法使用SAX是事件驱动的流解析技术
* 支持输入输出类型为SAXSource的请求
*/
@POST
@Path("sax")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public SAXSource getSAXSource(javax.xml.transform.sax.SAXSource saxSource) {
//需手动解析XML
return saxSource;
}

StAX
支持输入输出类型为StreamSource的请求。
/**
* getStreamSource()方法使用StAX拉式流解析技术
* 支持输入输出类型为StreamSource的请求
*/
@POST
@Path("stream")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public StreamSource getStreamSource(javax.xml.transform.stream.StreamSource streamSource) {
//需手动解析XML
return streamSource;
}

2)、JAXB标准
JAXP的缺点是需要编码解析XML,这增加了开发成本,但对业务逻辑的实现并没有实质的贡献。
JAXB只需要在POJO中定义相关的注解,使其和XML的schema对应,无须对XML进行程序式解析,弥补了JAXP的这一缺点,因此推荐使用JAXB作为XML解析的技术。
JAXB通过序列化和反序列化实现了XML数据和POJO对象的自动转换过程。在运行时,JAXB通过编组(marshall)过程将POJO序列化成ML格式的数据,通过解编(unmarshall)过程将XML格式的数据反序列化为Java对象。
JAXB的注解位于javax.xml.bind.annotation包中,从理论上讲,JAXB解析XML的性能不如JAXP,但使用JAXB的开发效率很高。
Jersey支持使用JAXBElement作为REST方法参数的形式,也支持使用POJO作为REST方法参数的形式,参数使用上有三种形式:
- JAXBElement
- POJO
- POJO字段作为子元素
- POJO字段作为元素属性
JAXBElement
@POST
@Path("element")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Book getElementEntity(JAXBElement<Book> bookElement) {
Book book = bookElement.getValue();
System.out.println(book);
return book;
}
实体:
@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 +
'}';
}
}

POJO字段作为子元素
@POST
@Path("pojo1")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Person getPojo1(Person person) {
System.out.println(person);
return person;
}
实体:
@XmlRootElement
public class Person {
private Long id;
private String name;
private Integer age;
@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 = "age")
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}

POJO字段作为元素属性
@POST
@Path("pojo2")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Person getPojo2(Person person) {
System.out.println(person);
return person;
}
实体:
@XmlRootElement
public class Person {
private Long id;
private String name;
private Integer age;
@XmlAttribute(name = "id")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@XmlAttribute(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlAttribute(name = "age")
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}

XMLConstnts.FEATURE_SECURE_PROCESSING
Jersey默认设置了XMLConstants.FEATURE SECURE PROCESSING属性,当属性或者元素过多时,会报“well-formedness eror”这样的警告信息。如果业务逻辑确实需要设计一个繁琐的POJO,可以通过设置
MessageProperties.XML SECURITY_DISABLE参数值为TRUE来屏蔽。
@Component
@ApplicationPath("/ws")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
property(MessageProperties.XML_SECURITY_DISABLE, Boolean.TRUE);
}
}
1.6、JSON类型
JSON类型已经成为Ajax技术中数据传输的实际标准。Jersey提供了4种处理JSON数据的媒体包。
3种解析流派:
- 基于POJO的JSON绑定
- 基于JAXB的JSON绑定
- 以及低级的(逐字的)JSON解析和处理)
4种技术:
MOXy和Jackon的处理方式相同,它们都不支持以JSON对象方式解析JSON数据,而是以绑定方式解析。Jettison支持以JSON对象方式解析JSON数据,同时支持JAXB方式的绑定。JSON-P就只支持JSON对象方式解析这种方式了。
| 解析方式/JSON支持包 | MOXy | JSON-P | Jackson | Jettison |
|---|---|---|---|---|
| POJO-based JSON Binding | 是 | 否 | 是 | 否 |
| JAXB-based JSON Binding | 是 | 否 | 是 | 是 |
| Low-level JSON parsing & processing | 否 | 是 | 否 | 是 |
1)、使用MOXy处理JSON
MOXy是EclipseLink项目的一个模块,MOXy实现了JSR222标准(JAXB2.2)和JSR235标准(SDO2.1.1)。同时,MOXy实现了JSR-353标准(Java API for Processing JSON1.0),以JAXB为基础来实现对JSR353的支持。
定义依赖
MOXy是Jersey默认的JSON解析方式,可以在项目中添加MOXy的依赖包来使用MOXy:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
定义Application
使用Servlet.3可以不定义web.xml配置。
MOXy的Feature接口实现类是MoxyJsonFeature,默认情况下,Jersey对其自动探测,无须在Applicaion类或其子类显式注册该类。如果不希望Jersey这种默认行为,可以通过设置如下属性来禁用自动探测:
CommonProperties.MOXY_JSON_FEATURE_DISABLE两端禁用ServerProperties..MOXY_JSON_FEATURE_DISABLE服务器端禁用ClientProperties.MOXY_JSON_FEATURE_DISABLE客户端禁用
@Component
@ApplicationPath("/api/*")
public class JsonResourceConfig extends ResourceConfig {
public JsonResourceConfig() {
//注册资源
register(StudentResource.class);
//取消MOXy自动探测
// property(CommonProperties.MOXY_JSON_FEATURE_DISABLE, true);
}
}
定义资源类
public class Student {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
如果EST应用处于多语言环境中,不要忘记统一开放接口的字符编码;如果统一开放接口同时供前端jsonp使用,不要忘记添加相关媒体类型,示例如下:
@Produces({"application/x-javascript;charset=UTF-8", "application/json;charset=UTF-8"})
@Path("stu")
//类上定义媒体类型后,方法中无需再定义
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class StudentResource {
private static List<Student> list = new LinkedList<>();
@POST
public Student addStudent(Student student) {
student.setId(System.currentTimeMillis());
list.add(student);
return student;
}
@GET
public List<Student> listStudents() {
System.out.println(list.size());
return list;
}
}
测试


客户端:
@Test
public void testMOXy() throws IOException {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://127.0.0.1:8080/api");
Student student = new Student();
student.setName("jerry");
student.setAge(17);
Response response = webTarget.path("stu")
.request()
.post(Entity.entity(student, MediaType.APPLICATION_JSON));
String result = response.readEntity(String.class);
System.out.println(result);
Client client2 = ClientBuilder.newClient();
WebTarget webTarget2 = client.target("http://127.0.0.1:8080/api");
Response response2 = webTarget.path("stu")
.request()
.get();
String result2 = response2.readEntity(String.class);
System.out.println(result2);
}
输出:
{"id":1703513139916,"name":"jerry","age":17}
[{"id":1703513139916,"name":"jerry","age":17}]
2)、使用JSON-P处理JSON
JSON-P的全称是Java API for JSON Processing(Java的JSON处理API),而不是JSON with padding(JSONP),两者只是名称相仿,用途大相径庭。JSON-P是JSR353标准
规范,用于统一Java处理JSON格式数据的API,其生产和消费的JSON数据以流的形式类似StAX处理XML,并为JSON数据建立Java对象模型,类似DOM。而JSONP是用于异步请求中传递脚本的回调函数来解决跨域问题。
定义依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
</dependency>
定义Application
使用JSON-P的应用,默认不需要在其Application中注册JsonProcessingFeature,除非使用了如下设置,依次用于在服务器和客户端两侧去活JSON-P功能、在服务器端去活
JSON-P功能、在客户端去活JSON-P功能:
CommonProperties.JSON_PROCESSING_FEATURE_DISABLEServerProperties.JSON_PROCESSING_FEATURE_DISABLEClientProperties.JSON_PROCESSING_FEATURE_DISABLE
JsonGenerator.PRETTY_PRINTING属性用于格式化JSON数据的输出,当属性值为TRUE时,MesageBodyReader和MessageBody Writer实例会对JSON数据进行额外处理,使得JSON数据可以格式化打印。该属性的设置在Application中:
@Component
@ApplicationPath("/api/*")
public class JsonResourceConfig extends ResourceConfig {
public JsonResourceConfig() {
//注册资源
register(StudentResource.class);
property(JsonGenerator.PRETTY_PRINTING, true);
}
}
定义资源类
@Path("stu")
//类上定义媒体类型后,方法中无需再定义
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class StudentResource {
private static List<Student> list = new LinkedList<>();
@POST
public Student addStudent(JsonObject jsonObject) {
Student student = new Student();
student.setId(System.currentTimeMillis());
student.setName(jsonObject.getString("name"));
student.setAge(jsonObject.getInt("age"));
list.add(student);
return student;
}
@GET
public JsonArray listStudents() {
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
for (Student stu : list) {
JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
objectBuilder.add("id", stu.getId());
objectBuilder.add("name", stu.getName());
objectBuilder.add("age", stu.getAge());
arrayBuilder.add(objectBuilder.build());
}
return arrayBuilder.build();
}
}
测试


3)、使用Jackson处理JSON
Jackson是一种流行的JSON支持技术,Jackson提供了3种JSON解析方式:
- 第一种是基于流式API的增量式解析/生成JSON的方式,读写JSON内容的过程是通过离散事件触发的,其底层基于StAX API读取JSON使用
org.codehaus.jackson.JsonParser,写入JSON使用org.codehaus.jackson.JsonGenerator - 第二种是基于树型结构的内存模型,提供一种不变式的JsonNode内存树模型,类似DOM树。
- 第三种是基于数据绑定的方式,
org.codehaus.jackson.map.ObjectMapper解析,使用JAXB的注解。
定义依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
定义Application
使用Jackson的应用,需要在其Application中注册JacksonFeature。同时,如果有必要根据不同的实体类做详细的解析,可以注册ContextResolver的实现类。
@Component
@ApplicationPath("/api/*")
public class JsonResourceConfig extends ResourceConfig {
public JsonResourceConfig() {
//注册资源
register(StudentResource.class);
register(JacksonFeature.class);
//注册ContextResolver的实现类JsonContextProvider
register(JsonContextProvider.class);
}
}
定义POJO
本例定义了3种不同方式的POJO,以演示Jackson处理JSON的多种方式:
- JsonStudent:仅用JAXB注解
- JsonHybridStudent:混用JAXB注解和Jackson注解
- JsonNoJaxbStudent:不使用注解
//仅用JAXB注解
@XmlRootElement
@XmlType(propOrder = {"id","name", "age"})
public class JsonStudent {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "JsonStudent{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//JAXB注解和Jackson注解混用
@XmlRootElement
public class JsonHybridStudent {
@JsonProperty("id")
private Long id;
@JsonProperty("name")
private String name;
@JsonProperty("age")
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "JsonHybridStudent{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//不使用任何注解
public class JsonNoJaxbStudent {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "JsonNoJaxbStudent{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
定义资源类
@Path("stu")
//类上定义媒体类型后,方法中无需再定义
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class StudentResource {
@Path("jaxb")
@GET
public JsonStudent getJsonStudent() {
JsonStudent jsonStudent = new JsonStudent();
jsonStudent.setId(1l);
jsonStudent.setName("jaxb");
jsonStudent.setAge(18);
return jsonStudent;
}
@Path("hybird")
@GET
public JsonHybridStudent getJsonHybridStudent() {
JsonHybridStudent jsonHybridStudent = new JsonHybridStudent();
jsonHybridStudent.setId(1l);
jsonHybridStudent.setName("hybird");
jsonHybridStudent.setAge(18);
return jsonHybridStudent;
}
@Path("nojaxb")
@GET
public JsonNoJaxbStudent getJsonNoJaxbStudent() {
JsonNoJaxbStudent jsonNoJaxbStudent = new JsonNoJaxbStudent();
jsonNoJaxbStudent.setId(1l);
jsonNoJaxbStudent.setName("nojaxb");
jsonNoJaxbStudent.setAge(18);
return jsonNoJaxbStudent;
}
}
上下文解析实现类
JsonContextProvider是ContextResolver(上下文解析器)的实现类,其作用是根据上下文提供的POO类型,分别提供两种解析方式。第一种是默认的方式,第二种是混合使用
Jackson和Jaxb。两种解析方式的示例代码如下。
@Provider
public class JsonContextProvider implements ContextResolver<ObjectMapper> {
final ObjectMapper d;
final ObjectMapper c;
public JsonContextProvider() {
this.d = createDefaultMapper();
this.c = createCombinedMapper();
}
@Override
public ObjectMapper getContext(Class<?> type) {
if (type == JsonHybridStudent.class) {
System.out.println("hybird");
return c;
} else {
System.out.println("other");
return d;
}
}
private static ObjectMapper createCombinedMapper() {
ObjectMapper result = new ObjectMapper();
result.setAnnotationIntrospectors(new JacksonAnnotationIntrospector(), new JaxbAnnotationIntrospector(TypeFactory.defaultInstance()));
return result;
}
private static ObjectMapper createDefaultMapper() {
ObjectMapper result = new ObjectMapper();
result.configure(SerializationFeature.INDENT_OUTPUT, true);
return result;
}
}
测试



4)、使用Jettison处理JSON
Jettison是一种使用StAX来解析JSON的实现。Jettison支持两种JSON映射到XML的方式:
- Jersey默认使用
MAPPED方式 - 另一种叫做
BadgerFish方式
定义依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jettison</artifactId>
</dependency>
定义Application
使用Jettison的应用,需要在其Application中注册JettisonFeature。同时,如果有必要根据不同的实体类做详细的解析,可以注册ContextResolver的实现类。
@Component
@ApplicationPath("/api/*")
public class JsonResourceConfig extends ResourceConfig {
public JsonResourceConfig() {
//注册资源
register(StudentResource.class);
register(JettisonFeature.class);
//注册ContextResolver的实现类JsonContextProvider
register(JettisonJsonContextResolver.class);
}
}
定义POJO
本例定义了两个类名不同、内容相同的POJO(Student1和Student2),用以演示Jettison对JSON数据以JETTISON_MAPPED(默认)和BADGERFISH两种不同方式的处理情况。
@XmlRootElement
public class Student1 {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student1{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@XmlRootElement
public class Student2 {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student1{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
定义资源类
@Path("stu")
//类上定义媒体类型后,方法中无需再定义
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class StudentResource {
@Path("mapped")
@GET
public Student1 getStudent1() {
Student1 student1 = new Student1();
student1.setId(1l);
student1.setName("mapped");
student1.setAge(18);
return student1;
}
@Path("badgerfish")
@GET
public Student2 getStudent2() {
Student2 student2 = new Student2();
student2.setId(1l);
student2.setName("badgerfish");
student2.setAge(18);
return student2;
}
}
上下文解析实现类
@Provider
public class JettisonJsonContextResolver implements ContextResolver<JAXBContext> {
private final JAXBContext c1;
private final JAXBContext c2;
public JettisonJsonContextResolver() throws JAXBException {
Class[] clz = new Class[]{Student1.class, Student2.class};
this.c1 = new JettisonJaxbContext(JettisonConfig.DEFAULT, clz);
this.c2 = new JettisonJaxbContext(JettisonConfig.badgerFish().build(), clz);
}
@Override
public JAXBContext getContext(Class<?> type) {
if (type == Student1.class) {
System.out.println("mapped");
return c1;
} else {
System.out.println("badgerfish");
return c2;
}
}
}
在这段代码中,JsonContextResolver定义了两种JAXBContext分别使用MAPPED方式或者BadgerFish方式。这两种方式的参数信息来自Jettision依赖包的
JettisonConfig类。在实现接口方法getContext()中,根据不同的POJO类型,返回两种JAXBContext实例之一。通过这样的实现,当流程获取JSON上下文时,既可使用Jettision依赖包完成对相关POJO的处理。
测试
注意Mapped和Badgerfish两种方式的JSON数据内容不同。


2、返回类型
2.1、void
在返回值类型是void的响应中,其响应实体为空,HTTP状态码为204。
@Path("return")
public class ReturnResource {
@Path("{s}")
@DELETE
public void delete(@PathParam("s") String s) {
System.out.println(s);
}
}
因为delete操作无须返回更多的关于资源表述的信息,因此该方法没有返回值,即返回值类型为void。
2.2、Response
在返回值类型为Response的响应中,响应实体为Response类的entity(方法定义的实体类实例。如果该内容为空,则HTTP状态码为204,否则HTTP状态码为200OK。
@Path("c")
@POST
public Response get(String s) {
//构建无返回值的响应实例,即响应状态码为204
// Response response = Response.noContent().build();
return Response.ok().entity(s).build();
}

2.3、GenericEntity
通用实体类型作为返回值的情况并不常用。其形式是构造一个统一的实体实例并将其返回,实体实例作为第一个参数、该实体类型作为第二个参数。
@Path("g")
@POST
public GenericEntity<String> get(byte[] bs) {
return new GenericEntity<String>(new String(bs), String.class);
}

2.4、File
@Path("f")
@POST
public File get(File f) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
String s = null;
while ((s = br.readLine()) != null) {
System.out.println(s);
}
}
return f;
}

2.5、自定义类型
@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 +
'}';
}
}
@Path("book")
@POST
@Produces(MediaType.APPLICATION_XML)
public Book get() {
Book book = new Book();
book.setId(1l);
book.setName("三国演义");
book.setPrice(33.45);
return book;
}

3、异常处理
3.1、处理状态码
HTTP常用状态码:
| 状态码 | 含义 |
|---|---|
200 OK | 服务器正常响应 |
201 Created | 创建新实体,响应头Location指定访问该实体的URL |
202 Accepted | 服务器接受请求,处理尚未完成。可用于异步处理机制 |
204 No Content | 服务器正常响应,但响应实体为空 |
301 Moved Permanently | 请求资源的地址发生永久变动,响应头Location指定新的URL |
302 Found | 请求资源的地址发生临时变动 |
304 Not Modified | 客户端缓存资源依然有效 |
400 Bad Request | 请求信息出现语法错误 |
401 Unauthorized | 请求资源无法授权给未验证用户 |
403 Forbidden | 请求资源未授权当前用户 |
404 Not Found | 请求资源不存在 |
405 Method Not Allowed | 请求方法不匹配 |
406 Not Acceptable | 请求资源的媒体类型不匹配 |
500 Internal Server Error | 服务器内部错误,意外终止响应 |
501 Not Implemented | 服务器不支持当前请求 |
JAX-RS2规定的REST式的Web服务的基本异常类型为运行时异常WebApplicationException类。该类包含3个主要的子类分别对应如下内容。
- HTTP状态码为3xx的重定向类
RedirectionException; - HTTP状态码为4xx的请求错误类
ClientErrorException; - HTTP状态码为5xx的服务器错误类
ServerErrorException;
它们各自的子类对照HTTP状态码再细分,比如常见的HTTP状态码404错误,对应的错误类为NotFoundException。
除了Jersey提供的标准异常类型,我们也可以根据业务需要自定义相关的业务异常类:
public class Jaxrs2GuideNotFoundException extends WebApplicationException {
public Jaxrs2GuideNotFoundException() {
//定义HTTP状态
super(Response.Status.NOT_FOUND);
}
public Jaxrs2GuideNotFoundException(String message) {
super(message);
}
}
3.2、ExceptionMapper
Jersey框架为我们提供了更为通用的异常处理方式。通过实现ExceptionMapper接口并使用@Provider注解将其定义为一个Provider,可以实现通用的异常的面向切面处理,而非针对某一个资源方法的异常处理,示例如下。
@Provider
public class EntityNotFoundMapper implements ExceptionMapper<Jaxrs2GuideNotFoundException> {
//拦截并返回新的响应实例
@Override
public Response toResponse(Jaxrs2GuideNotFoundException exception) {
return Response.status(404)
.entity("自定义的404:" + exception.getMessage())
.type("text/plain")
.build();
}
}
EntityNotFoundMapper实现了ExceptionMapper接口,并提供了泛型类型为前述刚定义的Jaxrs2GuideNotFoundException类;当响应中发生了
Jaxrs2GuideNotFoundException类型的异常,响应流程就会被拦截并补充HTTP状态码和异常消息,以文本作为媒体格式返回给客户端。
3.3、实例
使用上文中的自定义404异常Jaxrs2GuideNotFoundException和自定义切面处理EntityNotFoundMapper。
注册Provider:
@Component
@ApplicationPath("/ws")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
//注册单个资源类
register(ReturnResource.class);
//注册异常处理Provider
register(EntityNotFoundMapper.class);
//或直接声明资源包所在位置
// packages("pers.zhang.resource");
}
}
资源类:
@Path("return")
public class ReturnResource {
@Path("ex")
@GET
public void getEx() {
throw new Jaxrs2GuideNotFoundException("Jaxrs2GuideNotFoundException");
}
}
测试:

436

被折叠的 条评论
为什么被折叠?



