Jersey 客户端无法指定媒体类型
Jersey client can't specify media type
我有一个 get 呼叫休息服务(工作正常),我想更改它并使用球衣。
我从官方网站下载了最后的 JAR,并将它们放入我的项目中。
我所有的响应都抛出异常(除了这个:String response = invocationBuilder.get(String.class);
):
javax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1014)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:816)
at org.glassfish.jersey.client.JerseyInvocation.access0(JerseyInvocation.java:92)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:700)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:696)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:420)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:316)
代码:
ClientConfig clientConfig = new ClientConfig();
Client client = ClientBuilder.newClient(clientConfig);
//not so orthodox...
WebTarget webTarget = client.target(uri);//uri= https://www.foo.bar/api/v1.0/sms/sendMessage?accessKeyId=34TR&idCampaign=4&fromNumber=Test&toNumber=393281234567&message=Inserisci+la+seguente+password%3A+8c495b
Invocation.Builder invocationBuilder2 =
webTarget.request(MediaType.APPLICATION_JSON);
Invocation.Builder invocationBuilder =
webTarget.request();//------NO MEDIA TYPE SPECIFICATION
Invocation.Builder invocationBuilder3 =
webTarget.request(MediaType.APPLICATION_XML_TYPE);
Invocation.Builder invocationBuilder4 =
webTarget.request(MediaType.TEXT_XML_TYPE);
Invocation.Builder invocationBuilder5 =
webTarget.request(MediaType.TEXT_PLAIN_TYPE);
Invocation.Builder invocationBuilder6 =
webTarget.request(MediaType.APPLICATION_FORM_URLENCODED);
try {
String response2 = invocationBuilder2.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
//WORKS ONLY THIS, WITH NO MEDIA TYPE SPECIFICATION
String response = invocationBuilder.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
String response3 = invocationBuilder3.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
String response4 = invocationBuilder4.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
String response5 = invocationBuilder5.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
String response6 = invocationBuilder6.get(String.class);
} catch (Exception e) {System.out.println(e);}
//NONE OF THIS WORKS (last part of the test):
try {
SmsResponse response02 = invocationBuilder2.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response0 = invocationBuilder.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response03 = invocationBuilder3.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response04 = invocationBuilder4.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response05 = invocationBuilder5.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response06 = invocationBuilder6.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
响应字符串是JSON:{"status":"success","uid":"407077","numSms":1,"errorMsg":false}
但是我在尝试获取 SmsResponse 对象(代码的最后一部分)时遇到异常,response0 抛出了下面的异常(其他情况抛出之前的406异常):
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json;charset=UTF-8, type=class it.sian.zfab.SmsResponse, genericType=class it.sian.zfab.SmsResponse.
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:808)
at org.glassfish.jersey.client.JerseyInvocation.access0(JerseyInvocation.java:92)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:700)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:696)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:420)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:316)
豆子:
@XmlRootElement
public class SmsResponse implements Serializable {
private static final long serialVersionUID = 1L;
@QueryParam("uid")
private String uid;
@QueryParam("status")
private String status;
@QueryParam("errorMsg")
private String errorMsg;
@QueryParam("numSms")
private Integer numSms;
//getter and setter...
}
我的旧方法(有效),在这里你可以看到它对内容工作正常 application/json:
private <T> T restCallJson(String uri, Class<T> returnType)
{
T objectResponse = null;
try {
URL url = new URL(uri);
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type", "application/json");
InputStream inputStream = connection.getInputStream();
Gson gson = new Gson();
final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
objectResponse = gson.fromJson(reader, returnType);
connection.disconnect();
}
catch (Exception e)
{
log.error("WebServiceUtility - restCallJson: " + e);
}
return objectResponse;
}
1) 为什么不能指定MediaType?我应该使用哪一个?
2) 为什么我不能直接获取 SmsResponse 对象?
1) Why cannot I specify the MediaType? Which one should I use?
request(type)
设置 Accept: <type>
header 。如果服务器未设置为生成类型,则发送的正确响应为 406 Not Acceptable
。
例如 request(MediaType.APPLICATION_FORM_URLENCODED)
告诉服务器您希望以 application/www-x-form-urlencoded
形式返回数据。如果服务器无法为端点生成该媒体类型,您将返回 406。
如果服务器可以发送 JSON,那么 request(MediaType.APPLICATION_JSON)
应该 工作。
我会做的调试,而不是做
String reponse = invocationBuilder.get(String.class);
获取实际的 Response
object,并检查 header 和响应 body。
Response response = invocationBuilder.get();
int status = response.getStatus();
String body = response.readEntity(String.class);
除了调试之外,这样可以避免客户端出现异常。
这是什么
Invocation.Builder invocationBuilder = webTarget.request();
是将 Accept
header 设置为通配符 */*
,这意味着服务器可以发送它想要的任何类型。通常你不希望这样,因为客户端需要知道它返回的类型才能处理它。您可以做的调试是发送该通配符请求并取回响应。从那里您可以看到 Content-Type
header 以查看服务器发回的类型
Invocation.Builder invocationBuilder = webTarget.request();
Response response = invocationBuilder.get();
String contentType = response.getHeaderString("Content-Type");
从那里您可以看到应该为 Accept
header 设置什么类型。但是从你的第二个堆栈跟踪来看,服务器似乎正在发送 application/json
,所以没有理由 request("application/json")
不工作。
2) Why cannot I retrieve directly the SmsResponse object?
您需要一个 JSON 提供程序来处理从 JSON 到 SmsResponse
的反序列化。为此,您可以使用 Jackson 提供程序。希望您正在使用 Maven。你可以只添加这个依赖项
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
</dependency>
您无需执行任何其他操作。提供商自行注册。如果您不使用 Maven,请告诉我,我会 post 提供您需要的所有 jar。请告诉我您使用的 Jersey 版本。
旁白:
你应该明白Content-Type
和Accept
的区别。当客户端想要告诉服务器它想要返回什么类型时,客户端会发送一个 Accept
header。服务器可能无法生成该类型,在这种情况下,您将收到 406 Not Acceptable。
客户端使用Content-Type
来告诉服务器它正在发送什么类型的数据,例如POST,但一般不适用于GET,因为GET不发送任何数据。当服务器发回数据时,它总是设置 Content-Type
响应 header 来告诉客户端它返回的是什么类型。
您目前对 HttpURLConnection
的使用是错误的。您应该设置 Accept
header 而不是 Content-Type
header。它工作正常,因为它只是设置了通配符 Accept: */*
,而 Gson 并不关心 Content-Type
。
更新
刚刚新建了一个Maven项目,只添加了上面的依赖。它具有以上所有的传递依赖性。但大多数已经带有 Jersey 发行版。无论你没有什么,那就是你应该寻找的。主要是杰克逊相关的罐子。
我有一个 get 呼叫休息服务(工作正常),我想更改它并使用球衣。
我从官方网站下载了最后的 JAR,并将它们放入我的项目中。
我所有的响应都抛出异常(除了这个:String response = invocationBuilder.get(String.class);
):
javax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1014)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:816)
at org.glassfish.jersey.client.JerseyInvocation.access0(JerseyInvocation.java:92)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:700)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:696)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:420)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:316)
代码:
ClientConfig clientConfig = new ClientConfig();
Client client = ClientBuilder.newClient(clientConfig);
//not so orthodox...
WebTarget webTarget = client.target(uri);//uri= https://www.foo.bar/api/v1.0/sms/sendMessage?accessKeyId=34TR&idCampaign=4&fromNumber=Test&toNumber=393281234567&message=Inserisci+la+seguente+password%3A+8c495b
Invocation.Builder invocationBuilder2 =
webTarget.request(MediaType.APPLICATION_JSON);
Invocation.Builder invocationBuilder =
webTarget.request();//------NO MEDIA TYPE SPECIFICATION
Invocation.Builder invocationBuilder3 =
webTarget.request(MediaType.APPLICATION_XML_TYPE);
Invocation.Builder invocationBuilder4 =
webTarget.request(MediaType.TEXT_XML_TYPE);
Invocation.Builder invocationBuilder5 =
webTarget.request(MediaType.TEXT_PLAIN_TYPE);
Invocation.Builder invocationBuilder6 =
webTarget.request(MediaType.APPLICATION_FORM_URLENCODED);
try {
String response2 = invocationBuilder2.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
//WORKS ONLY THIS, WITH NO MEDIA TYPE SPECIFICATION
String response = invocationBuilder.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
String response3 = invocationBuilder3.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
String response4 = invocationBuilder4.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
String response5 = invocationBuilder5.get(String.class);
} catch (Exception e) {System.out.println(e);}
try {
String response6 = invocationBuilder6.get(String.class);
} catch (Exception e) {System.out.println(e);}
//NONE OF THIS WORKS (last part of the test):
try {
SmsResponse response02 = invocationBuilder2.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response0 = invocationBuilder.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response03 = invocationBuilder3.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response04 = invocationBuilder4.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response05 = invocationBuilder5.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
try {
SmsResponse response06 = invocationBuilder6.get(SmsResponse.class);
} catch (Exception e) {System.out.println(e);}
响应字符串是JSON:{"status":"success","uid":"407077","numSms":1,"errorMsg":false}
但是我在尝试获取 SmsResponse 对象(代码的最后一部分)时遇到异常,response0 抛出了下面的异常(其他情况抛出之前的406异常):
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json;charset=UTF-8, type=class it.sian.zfab.SmsResponse, genericType=class it.sian.zfab.SmsResponse.
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:808)
at org.glassfish.jersey.client.JerseyInvocation.access0(JerseyInvocation.java:92)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:700)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:696)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:420)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:316)
豆子:
@XmlRootElement
public class SmsResponse implements Serializable {
private static final long serialVersionUID = 1L;
@QueryParam("uid")
private String uid;
@QueryParam("status")
private String status;
@QueryParam("errorMsg")
private String errorMsg;
@QueryParam("numSms")
private Integer numSms;
//getter and setter...
}
我的旧方法(有效),在这里你可以看到它对内容工作正常 application/json:
private <T> T restCallJson(String uri, Class<T> returnType)
{
T objectResponse = null;
try {
URL url = new URL(uri);
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type", "application/json");
InputStream inputStream = connection.getInputStream();
Gson gson = new Gson();
final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
objectResponse = gson.fromJson(reader, returnType);
connection.disconnect();
}
catch (Exception e)
{
log.error("WebServiceUtility - restCallJson: " + e);
}
return objectResponse;
}
1) 为什么不能指定MediaType?我应该使用哪一个?
2) 为什么我不能直接获取 SmsResponse 对象?
1) Why cannot I specify the MediaType? Which one should I use?
request(type)
设置 Accept: <type>
header 。如果服务器未设置为生成类型,则发送的正确响应为 406 Not Acceptable
。
例如 request(MediaType.APPLICATION_FORM_URLENCODED)
告诉服务器您希望以 application/www-x-form-urlencoded
形式返回数据。如果服务器无法为端点生成该媒体类型,您将返回 406。
如果服务器可以发送 JSON,那么 request(MediaType.APPLICATION_JSON)
应该 工作。
我会做的调试,而不是做
String reponse = invocationBuilder.get(String.class);
获取实际的 Response
object,并检查 header 和响应 body。
Response response = invocationBuilder.get();
int status = response.getStatus();
String body = response.readEntity(String.class);
除了调试之外,这样可以避免客户端出现异常。
这是什么
Invocation.Builder invocationBuilder = webTarget.request();
是将 Accept
header 设置为通配符 */*
,这意味着服务器可以发送它想要的任何类型。通常你不希望这样,因为客户端需要知道它返回的类型才能处理它。您可以做的调试是发送该通配符请求并取回响应。从那里您可以看到 Content-Type
header 以查看服务器发回的类型
Invocation.Builder invocationBuilder = webTarget.request();
Response response = invocationBuilder.get();
String contentType = response.getHeaderString("Content-Type");
从那里您可以看到应该为 Accept
header 设置什么类型。但是从你的第二个堆栈跟踪来看,服务器似乎正在发送 application/json
,所以没有理由 request("application/json")
不工作。
2) Why cannot I retrieve directly the SmsResponse object?
您需要一个 JSON 提供程序来处理从 JSON 到 SmsResponse
的反序列化。为此,您可以使用 Jackson 提供程序。希望您正在使用 Maven。你可以只添加这个依赖项
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
</dependency>
您无需执行任何其他操作。提供商自行注册。如果您不使用 Maven,请告诉我,我会 post 提供您需要的所有 jar。请告诉我您使用的 Jersey 版本。
旁白:
你应该明白Content-Type
和Accept
的区别。当客户端想要告诉服务器它想要返回什么类型时,客户端会发送一个 Accept
header。服务器可能无法生成该类型,在这种情况下,您将收到 406 Not Acceptable。
客户端使用Content-Type
来告诉服务器它正在发送什么类型的数据,例如POST,但一般不适用于GET,因为GET不发送任何数据。当服务器发回数据时,它总是设置 Content-Type
响应 header 来告诉客户端它返回的是什么类型。
您目前对 HttpURLConnection
的使用是错误的。您应该设置 Accept
header 而不是 Content-Type
header。它工作正常,因为它只是设置了通配符 Accept: */*
,而 Gson 并不关心 Content-Type
。
更新
刚刚新建了一个Maven项目,只添加了上面的依赖。它具有以上所有的传递依赖性。但大多数已经带有 Jersey 发行版。无论你没有什么,那就是你应该寻找的。主要是杰克逊相关的罐子。