Resteasy javax – 如何在服务器端将 content-type */* 映射为 application/json?

huangapple go评论78阅读模式
英文:

Resteasy javax - How to map content-type */* to application/json on server-side?

问题

问题:我无法通过更改服务器端的代码而不更改客户端代码,将客户端消息中的内容类型*/*映射到服务器端的application/json。(部署的客户端太多,不能做后者)

在 WildFly 10 上,一切都正常(使用*/*),但在 WildFly 14/18 上失败,显示如下错误:

RESTEASY002010: 执行失败: javax.ws.rs.NotSupportedException: RESTEASY003200: 无法找到类型为: class xxxx 的消息主体读取器,内容类型为: */*,位于 org.jboss.resteasy.resteasy-jaxrs@3.9.1.Final//org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.throwReaderNotFound

上述所有内容均基于 Java 8。以下代码显示了在不更改服务器端代码的情况下,使 WF14+ 能够正常工作的客户端更改:

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "*/*");  // 对 WF10 有效,对 WF14+ 无效
// conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); // 对所有情况都有效

适用于服务器端的代码如下:

@POST
@Path("fileDownload")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_OCTET_STREAM)
// 尝试的其他消耗选项仍然失败:
// @Consumes({ MediaType.APPLICATION_JSON, MediaType.WILDCARD, MediaType.APPLICATION_OCTET_STREAM, "*/*", "*/*\r\n", "*/*; charset=UTF-8" } )
public Response fileDownload(@HeaderParam("Range") String range, FileDownloadRequest fileDwnReq) throws IOException { ... }

我进行了网络搜索,所有结果都指向客户端的更改(这将起作用),但我们必须更改服务器端或继续使用 WF10。我还尝试了在 web.xml 中设置字符集行为或映射媒体类型,但没有任何区别。例如:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>MyRestServices</display-name>
    <listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
    </listener-class>
    </listener>
    
    <!--   
    要指定先前的行为,在其中对文本媒体类型使用UTF-8,但不附加显式字符集,可以将上下文参数“resteasy.add.charset”设置为“false”。默认为“true”。
    参见 https://docs.jboss.org/resteasy/docs/3.1.2.Final/userguide/html_single/index.html
    这没有起作用。
     -->
<!--<context-param>-->
<!--<param-name>resteasy.add.charset</param-name>-->
<!--<param-value>false</param-value>-->
<!--</context-param>-->
        
     <context-param>
        <param-name>resteasy.media.type.mappings</param-name>
        <param-value>*/* : application/json</param-value>
<!--<param-value>html : text/html, json : application/json, xml : application/xml</param-value>-->
    </context-param>

我被难住了。任何建议/指导都将不胜感激。

编辑:以下是失败的 HTTP 请求的转储:

URI=/xxx/fileDownload
characterEncoding=null
contentLength=94
contentType=[*/*]
header=Accept=text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
header=Connection=keep-alive
header=Content-Type=*/*
header=Content-Length=94
header=User-Agent=Java/1.8.0_152
header=Host=127.0.0.1:8014
locale=[]
method=POST
protocol=HTTP/1.1
queryString=
remoteAddr=/127.0.0.1:59867
remoteHost=127.0.0.1
scheme=http
host=127.0.0.1:8014
serverPort=8014
isSecure=false
--------------------------RESPONSE--------------------------
contentLength=0
contentType=null
header=Connection=keep-alive
header=Content-Length=0
header=Date=Thu, 20 Aug 2020 13:36:44 GMT
status=415
英文:

Problem: I cannot get a content-type of */* in the client message to map to application/json on the server side by changing the server code and not the client code. (Too many deployed clients to do the latter)

On WildFly 10 everything works as is (with the */*), but on WildFly 14/18 it fails with

RESTEASY002010: Failed to execute: javax.ws.rs.NotSupportedException: RESTEASY003200: Could not find message body reader for type: class xxxx of content type: */* at org.jboss.resteasy.resteasy-
jaxrs@3.9.1.Final//org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.throwReaderNotFound

All the above is on Java 8. The code below shows the client-side change that would get WF14+ to work without server-side edits:

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(&quot;POST&quot;);
conn.setRequestProperty(&quot;Content-Type&quot;, &quot;*/*&quot;);  // works for WF10, not WF14+
// conn.setRequestProperty(&quot;Content-Type&quot;, &quot;application/json; charset=UTF-8&quot;); // works for all

The applicable server side code is as follows:

@POST
@Path(&quot;fileDownload&quot;)
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_OCTET_STREAM)
// Consume options tried that still failed:
// @Consumes({ MediaType.APPLICATION_JSON, MediaType.WILDCARD, MediaType.APPLICATION_OCTET_STREAM, &quot;*/*&quot;, &quot;*/*\r\n&quot;, &quot;*/*; charset=UTF-8&quot; } )
public Response fileDownload(@HeaderParam(&quot;Range&quot;) String range, FileDownloadRequest fileDwnReq) throws IOException { ... }

My net searches all point to client-side changes (which will work) but we have to change the server-side or stay on WF10. I have also tried setting charset behaviour or mapping the media type in the web.xml but it made no difference. For example:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
&lt;web-app xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; xmlns:web=&quot;http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot; xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot; id=&quot;WebApp_ID&quot; version=&quot;2.5&quot;&gt;
  &lt;display-name&gt;MyRestServices&lt;/display-name&gt;
    &lt;listener&gt;
    &lt;listener-class&gt;org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
    &lt;/listener-class&gt;
    &lt;/listener&gt;
    
    &lt;!--   
    To specify the previous behavior, in which UTF-8 was used for text media types, but 
    the explicit charset was not appended, the context parameter &quot;resteasy.add.charset&quot; may be 
    set to &quot;false&quot;. It defaults to &quot;true&quot;. 
    See https://docs.jboss.org/resteasy/docs/3.1.2.Final/userguide/html_single/index.html
    This did not work.
     --&gt;
&lt;!--     &lt;context-param&gt; --&gt;
&lt;!--        &lt;param-name&gt;resteasy.add.charset&lt;/param-name&gt; --&gt;
&lt;!--        &lt;param-value&gt;false&lt;/param-value&gt; --&gt;
&lt;!--     &lt;/context-param&gt; --&gt;
        
     &lt;context-param&gt;
        &lt;param-name&gt;resteasy.media.type.mappings&lt;/param-name&gt;
        &lt;param-value&gt;*/* : application/json&lt;/param-value&gt;
&lt;!-- &lt;param-value&gt;html : text/html, json : application/json, xml : application/xml&lt;/param-value&gt; --&gt;
    &lt;/context-param&gt;

I am stumped. Any suggestions/pointers will be appreciated.

Edit: Below is a dump of the failing http request:

 URI=/xxx/fileDownload
 characterEncoding=null
     contentLength=94
       contentType=[*/*]
            header=Accept=text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
            header=Connection=keep-alive
            header=Content-Type=*/*
            header=Content-Length=94
            header=User-Agent=Java/1.8.0_152
            header=Host=127.0.0.1:8014
            locale=[]
            method=POST
          protocol=HTTP/1.1
       queryString=
        remoteAddr=/127.0.0.1:59867
        remoteHost=127.0.0.1
            scheme=http
              host=127.0.0.1:8014
        serverPort=8014
          isSecure=false
--------------------------RESPONSE--------------------------
     contentLength=0
       contentType=null
            header=Connection=keep-alive
            header=Content-Length=0
            header=Date=Thu, 20 Aug 2020 13:36:44 GMT
            status=415

答案1

得分: 2

我通过添加一个ContainerRequestFilter来改变匹配之前的头部,找到了一种方法。以下代码起到了作用:

```java
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.container.PreMatching;

@Provider
@PreMatching
// 将 */* 映射为 JSON 用于 /filedownload
public final class ContentFilter implements ContainerRequestFilter {
   @Override
   public void filter(ContainerRequestContext ctx) throws IOException {
      UriInfo uri = ctx.getUriInfo();
      if ((uri != null) && uri.getPath().toLowerCase().contains("filedownload")) {
          String ctp = ctx.getHeaderString("Content-Type");
          if ("*/*".equals(ctp))
             ctx.getHeaders().putSingle("Content-Type", "application/json; charset=UTF-8");
      }
   }
}

我还需要在我的 web.xml 中添加以下内容:

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>xxx.yyy.ContentFilter</param-value>
</context-param>

<details>
<summary>英文:</summary>

I found a way by adding a ContainerRequestFilter that changes the header before matching. Below did the trick:

import javax.ws.rs.core.UriInfo;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.container.PreMatching;

@Provider
@PreMatching
// Mapp / to JSON for /filedownload
public final class ContentFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext ctx) throws IOException {
UriInfo uri = ctx.getUriInfo();
if ((uri != null) && uri.getPath().toLowerCase().contains("filedownload")) {
String ctp = ctx.getHeaderString("Content-Type");
if ("/".equals(ctp))
ctx.getHeaders().putSingle("Content-Type", "application/json; charset=UTF-8");
}
}
}


I also had to add the following to my web.xml

<context-param>
<param-name>resteasy.providers</param-name>
<param-value>xxx.yyy.ContentFilter</param-value>
</context-param>

 

</details>



# 答案2
**得分**: 0

在客户端使用通配符在“Content-Type”中是没有意义的。

*客户端* 可能会接受多种格式的服务器响应(使用“Accept”标头)。

*服务器* 可能会接受(消耗)多种媒体,但是客户端必须知道他实际上在发布什么类型的数据。如果您不告诉服务器您在HTTP正文中发送了什么,服务器可能无法(始终)猜测。

<details>
<summary>英文:</summary>

Using wildcards in &quot;Content-Type&quot; on the client-side makes no sense.

The *client* may accept server responses in multiple format (using the &quot;Accept&quot; header)

The *server* may accept (consume) multiple media, but the client has to know what kind of data he is effectively posting. If you do not tell the server what you are sending in the http body, the server cannot (always) guess it.



</details>



# 答案3
**得分**: 0

根据[文档][1],你应该能够将请求正文作为String参数获取,并随后手动将其转换为所需的对象形式。

  [1]: https://docs.jboss.org/resteasy/docs/4.5.6.Final/userguide/html/Content_Marshalling_Providers.html#Default_Providers_and_default_JAX-RS_Content_Marshalling

<details>
<summary>英文:</summary>

According to the [documentation][1] you should be able get the request body as a String parameter and subsequently marshal it manually to your desired object form.

  [1]: https://docs.jboss.org/resteasy/docs/4.5.6.Final/userguide/html/Content_Marshalling_Providers.html#Default_Providers_and_default_JAX-RS_Content_Marshalling

</details>



huangapple
  • 本文由 发表于 2020年8月20日 20:24:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/63505043.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定