【问题标题】:Jersey client can't specify media typeJersey 客户端无法指定媒体类型
【发布时间】:2016-01-17 19:48:42
【问题描述】:

我接到了休息服务的电话(工作正常),我想更改它并使用球衣。 我从官方网站下载了最后一个 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)

豆子:

@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...
}

我的旧方法(有效),在这里您可以看到它在内容应用程序/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 对象?

【问题讨论】:

    标签: java json jersey-2.0 jersey-client


    【解决方案1】:

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

    request(type) 设置 Accept: &lt;type&gt; 标头。如果服务器未设置为生成该类型,则发送的正确响应为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();
    

    确实将Accept 标头设置为通配符*/*,这意味着服务器可以发送它想要的任何类型。通常你不希望这样,因为客户端需要知道它返回的类型才能处理它。您可以做的调试是发送该通配符请求并取回响应。从那里您可以看到 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。请告诉我您使用的泽西岛版本。

    旁白:

    您应该了解Content-TypeAccept 之间的区别。当客户端想要告诉服务器它想要返回什么类型时,客户端会发送一个Accept 标头。服务器可能无法生成该类型,在这种情况下您将收到 406 Not Acceptable。

    Content-Type 被客户端用来告诉服务器它正在发送什么类型的数据,例如使用 POST,但通常不适用于 GET,因为 GET 不发送任何数据。当服务器发回数据时,它总是设置Content-Type响应头来告诉客户端它返回的是什么类型。

    您当前对HttpURLConnection 的使用不正确。而不是 Content-Type 标头,您应该设置 Accept 标头。它工作得很好,因为它只是设置了通配符Accept: */*,而 Gson 并不关心Content-Type


    更新

    我刚刚创建了一个新的 Maven 项目,并且只添加了上述依赖项。它具有上述所有传递依赖项。但大多数已经随 Jersey 发行版一起提供。无论你没有什么,这就是你应该寻找的。主要是杰克逊相关的罐子。

    【讨论】:

    • status是200,body就是我之前发的json字符串,response的context里面的headers是:{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]}
    • Content-Typeapplication/json。因此,我想不出任何request("application/json") 导致 406 的原因。
    • invocationBuilder2.get()的正文是&lt;!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"&gt; &lt;html&gt;&lt;head&gt; &lt;title&gt;406 Not Acceptable&lt;/title&gt; &lt;/head&gt;&lt;body&gt; &lt;h1&gt;Not Acceptable&lt;/h1&gt; &lt;p&gt;An appropriate representation of the requested resource /api/v1.0/sms/sendMessage could not be found on this server.&lt;/p&gt; Available variants: &lt;ul&gt; &lt;li&gt;&lt;a href="sendMessage.php"&gt;sendMessage.php&lt;/a&gt; , type text/html&lt;/li&gt; &lt;/ul&gt; &lt;hr&gt; &lt;address&gt;Apache/2.2.15 (CentOS) Server at www.foo.bar Port 443&lt;/address&gt; &lt;/body&gt;&lt;/html&gt;
    • 标题是:{Alternates=[{"sendMessage.php" 1 {type text/html}}], Date=[Mon, 19 Oct 2015 13:41:47 GMT], Vary=[negotiate], TCN=[list], Content-Length=[461], Content-Type=[text/html; charset=iso-8859-1], Connection=[close], Server=[Apache/2.2.15 (CentOS)]}
    • 您可以尝试两种方法:request("application/json; charset=UTF-8")request().accept("application/json")(这与仅执行 request("application/json") 没有什么不同。如果两者都不起作用,则服务器有问题,无法处理 @987654362 @ 标头。只需使用通配符。只要服务器将Content-Type 标头作为application/json 发回并且您拥有JSON 提供程序,那么response.readEntity(SmsResponse.class) 应该返回SmsResponse 就好了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-01
    • 2023-03-17
    • 2015-08-21
    相关资源
    最近更新 更多