JAX-RS API请求与响应头处理实例解析
在JAX-RS API开发中,处理请求头和响应头是一项常见的任务。本文将通过一系列实例,详细解析如何在JAX-RS中访问和设置请求头,以及如何返回响应头。
访问请求头
HttpHeaders示例
我们可以通过HttpHeaders
接口访问所有的请求头。使用@Context
注解可以注入HttpHeaders
的实例:
@Path("/")
public class TestResource {
@GET
@Path("test1")
public String allHeaders(@Context HttpHeaders headers) {
MultivaluedMap<String, String> rh = headers.getRequestHeaders();
String str = rh.entrySet()
.stream()
.map(e -> e.getKey() + " = " + e.getValue())
.collect(Collectors.joining("<br/>"));
return str;
}
}
要运行示例,可以在项目的pom.xml
中配置嵌入式Tomcat:
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
</configuration>
</plugin>
然后运行命令mvn tomcat7:run
启动服务。
HttpHeaders
类还提供了许多有用的强类型方法来获取特定的请求头,例如getAcceptableLanguages()
, getCookies()
, getLength()
等。以下是一个获取Accept-Language
请求头值的简单示例:
@Path("/")
public class TestResource {
@GET
@Path("test2")
public String acceptableLanguages(@Context HttpHeaders headers) {
List<Locale> locales = headers.getAcceptableLanguages();
return locales.stream()
.map(l -> l.toString())
.collect(Collectors.joining("<br/>"));
}
}
使用@HeaderParam注解
@HeaderParam
注解可以将请求头的值绑定到资源方法的参数上:
@Path("/")
public class TestResource {
@GET
@Path("test3")
public String requestParamTest1(@HeaderParam(HttpHeaders.HOST) String host) {
return "The request 'host' header value = " + host;
}
}
自动类型绑定
JAX-RS可以隐式地将请求头字符串绑定到合适的Java类型值上。以下是一些示例:
绑定到Java基本类型
@Path("/")
public class TestResource {
@GET
@Path("test4")
public String requestParamTest2(@HeaderParam("anInt") int anInteger) {
return "The request header 'anInt' value: " + anInteger;
}
}
我们可以使用HTTPie工具发送自定义的anInt
头:
http --header "anInt:123" http://localhost:8080/test4
绑定到具有String接受构造函数的Java类型
具有String
接受构造函数的类型,并且被@HeaderParam
注解标记的类型,可以自动使用目标请求头进行初始化。例如java.util.Date
:
new Date("Fri, 3 March 2017 20:35:00 GMT");
@Path("/")
public class TestResource {
@GET
@Path("test5")
public String requestParamTest3(@HeaderParam("aDate") Date date) {
return "The request header 'aDate' value: " + date;
}
}
绑定到具有valueOf(String)工厂方法的类型
具有以下签名的方法的类型T
也可以使用:
public static T valueOf(String s) { .. }
Java中有多种类具有此方法,例如Integer.valueOf(..)
, Long.valueOf(..)
等。自定义类型也可以使用。以下示例创建了一个自定义类型来演示用法:
public class DateInfo {
private Date date;
public static DateInfo valueOf(String dateStr) {
if(dateStr==null){
return null;
}
DateInfo dateInfo = new DateInfo();
dateInfo.date = new Date(dateStr);
return dateInfo;
}
public LocalDateTime asLocalDateTime() {
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
public Date asDate() {
return date;
}
}
@Path("/")
public class TestResource {
@GET
@Path("test6")
public String valueOfTest(@HeaderParam("aDate") DateInfo dateInfo) {
return "The request header 'aDate' converted to LocalDateTime: " +
dateInfo.asLocalDateTime();
}
}
绑定到实现ParamConverter接口的类型
一般来说,实现javax.ws.rs.ext.ParamConverter
接口的实现可以绑定任何HTTP消息属性(@HeaderParam
, @QueryParam
, @PathParam
, @MatrixParam
等)到相应的合适的Java类型(预定义或用户定义)。以下示例展示了如何直接转换LocalDateTime
(与上一个示例相比):
public class MyDateConverter implements ParamConverter<LocalDateTime> {
@Override
public LocalDateTime fromString(String value) {
Date date = new Date(value);
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
@Override
public String toString(LocalDateTime value) {
return value.toString();
}
}
我们还需要实现ParamConverterProvider
并用@Provider
注解标记,以便我们的自定义转换器可以在部署时被发现和注册。
@Provider
public class MyDateConverterProvider implements ParamConverterProvider {
@Override
public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType,
Annotation[] annotations) {
if (rawType == LocalDateTime.class) {
return (ParamConverter<T>) new MyDateConverter();
}
return null;
}
}
相应的资源方法如下:
@Path("/")
public class TestResource {
@GET
@Path("test7")
public String paramConverterTest(@HeaderParam("aDate") LocalDateTime date) {
return "The request header 'aDate' converted by MyDateConverter: " + date;
}
}
绑定到java.util.Collection
我们可以使用集合(java.util.List, java.util.Set, java.util.SortedSet)作为资源方法参数。集合中的元素可以是我们之前讨论的任何类型。集合实例是只读的。
@Path("/")
public class TestResource {
@GET
@Path("test8")
public String collectionTest(@HeaderParam("myHeader") List<String> list) {
String rv = "header collection values: \n";
rv += list.stream()
.map(Object::toString)
.collect(Collectors.joining("\n"));
return rv;
}
}
返回响应头
需要提供响应附加元数据的方法应该返回Response
实例。ResponseBuilder
类提供了一种方便的方式来使用构建者模式创建Response
实例。以下示例展示了如何使用ResponseBuilder
添加响应头:
@Path("/")
public class TestResource {
@GET
@Path("test10")
public Response responseHeaderTest1() {
Response.ResponseBuilder rb = Response.ok("the test response");
Response response = rb.header("header1", "value1")
.header("header2", "value2")
.build();
return response;
}
}
使用JAX-RS客户端发送请求头
以下示例展示了如何使用JAX-RS客户端API向资源服务发送请求头。
@Path("/")
public class TestResource {
@GET
@Path("test10")
public Response responseHeaderTest1() {
Response.ResponseBuilder rb = Response.ok("the test response");
Response response = rb.header("header1", "value1")
.header("header2", "value2")
.build();
return response;
}
@GET
@Path("test11")
public String clientTest(@Context HttpHeaders headers) {
MultivaluedMap<String, String> rh = headers.getRequestHeaders();
String str = rh.entrySet()
.stream()
.map(e -> e.getKey() + " = " + e.getValue())
.collect(Collectors.joining("\n"));
return str;
}
}
public class MyClient {
public static void main(String[] args) {
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8080/test11");
Invocation.Builder builder = target.request();
String response = builder.header("myHeader", "a")
.header("aDateHeader", new Date())
.get(String.class);
System.out.println(response);
client.close();
}
}
输出
myheader = [a]
adateheader = [Sun, 19 Mar 2017 17:13:28 GMT]
user-agent = [Jersey/2.25