Jersey客户端无法指定媒体类型。

4

我有一个调用rest服务的get请求(能够正常工作),我想要更改并使用Jersey。 我从官方网站下载了最新的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.access$700(JerseyInvocation.java:92)
    at org.glassfish.jersey.client.JerseyInvocation$2.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);}

response字符串是一个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.access$700(JerseyInvocation.java:92)
    at org.glassfish.jersey.client.JerseyInvocation$2.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)

豆子(The bean):
@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个回答

5

1) 为什么我不能指定MediaType?我应该使用哪个?

request(type) 设置了 Accept: <type> 头。如果服务器没有设置生成这种类型,那么正确的响应是发送 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 对象,并检查标头和响应正文。
Response response = invocationBuilder.get();
int status = response.getStatus();
String body = response.readEntity(String.class);

除了调试外,这将避免客户端发生异常。
这是什么?
Invocation.Builder invocationBuilder = webTarget.request();

它是否将“接受”标头设置为通配符“*/*”,这意味着服务器可以发送任何类型。通常情况下,您不希望这样做,因为客户端需要知道返回的类型才能处理它。您可以执行以下操作进行调试:发送通配符请求并获取响应。从那里,您可以查看“Content-Type”标头以查看服务器返回的类型。
Invocation.Builder invocationBuilder = webTarget.request();
Response response = invocationBuilder.get();
String contentType = response.getHeaderString("Content-Type");

从那里,您可以看到应该为Accept标头设置什么类型。但是从您的第二个堆栈跟踪中看,似乎服务器正在发送application/json,因此没有理由request("application/json")不起作用。

2)为什么我不能直接检索SmsResponse对象?

您需要一个可以处理从JSON反序列化为SmsResponse的JSON提供程序。为此,您可以使用Jackson提供程序。希望您正在使用Maven。您只需添加此依赖项即可。

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>${jersey2.version}</version>
</dependency>

您不需要做任何其他事情。提供程序会自行注册。如果您没有使用Maven,请告诉我,我会发布您所需的所有jar包。只需让我知道您正在使用的Jersey版本即可。
顺便说一下:
您应该了解“Content-Type”和“Accept”之间的区别。当客户端想要告诉服务器它想要什么类型的返回内容时,它会发送一个“Accept”头。服务器可能无法生成该类型,此时您将收到406 Not Acceptable。
“Content-Type”由客户端用于告诉服务器它正在发送的数据类型,例如POST,但通常不用于GET,因为GET不发送任何数据。当服务器发送数据回来时,它总是设置“Content-Type”响应头,以告诉客户端它正在获取什么类型的数据。
您当前使用的“HttpURLConnection”是不正确的用法。您应该设置“Accept”头,而不是“Content-Type”头。它可以正常工作,因为它只设置通配符“Accept: */*”,而Gson并不关心“Content-Type”。

更新

enter image description here

我刚创建了一个新的Maven项目,并且只添加了上述依赖项。它具有所有上述的传递依赖项。但是,大多数已经随着Jersey分发而来。您所需要的是找到您没有的部分。主要与Jackson相关的jar文件。

状态码为200,响应正文就像我之前发布的JSON字符串一样,响应报头包含以下内容:{Content-Length=[63], Expires=[Thu, 19 Nov 1981 08:52:00 GMT], Set-Cookie=[PHPSESSID=omntv5nt9ekjlqoh949diaoqp3; path=/], Connection=[close], X-Powered-By=[PHP/5.3.3], Server=[Apache/2.2.15 (CentOS)], Pragma=[no-cache], Cache-Control=[no-store, no-cache, must-revalidate, post-check=0, pre-check=0], Vary=[negotiate], Date=[Mon, 19 Oct 2015 13:28:12 GMT], TCN=[choice], Content-Location=[sendMessage.php], Content-Type=[application/json; charset=UTF-8]} - Accollativo
invocationBuilder2.get() 的主体是 `<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head> <title>406 Not Acceptable</title> </head><body> <h1>Not Acceptable</h1> <p>An appropriate representation of the requested resource /api/v1.0/sms/sendMessage could not be found on this server.</p> Available variants: <ul> <li><a href="sendMessage.php">sendMessage.php</a> , type text/html</li> </ul> <hr> <address>Apache/2.2.15 (CentOS) Server at www.foo.bar Port 443</address> </body></html>`。 - Accollativo
就像我说的那样,服务器出了问题。这是服务器的问题,它没有适当地遵守HTTP协议。只需使用通配符request()即可。您仍将得到Content-Type: application/json。这就是您需要能够反序列化为SmsResponse的全部。 - Paul Samsotha
我找到问题的第一部分:我的uri在sendMessage后面缺少了**.PHP**!!! 正确的uri是:https://www.foo.bar/api/v1.0/sms/sendMessage.php?accessKeyId=34rt&idCampaign=4&fromNumber=Test&toNumber=393281234567&message=Inserisci+la+seguente+password%3A+e71a3b 这样json头就被接受了。 谢谢,明天我会检查你对第二部分的建议。 - Accollativo
我下载了这个jar包:http://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson/2.22 但是我仍然无法获取SmsResponse对象,出现了之前的错误(找不到MessageBodyReader)。 - Accollativo
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接