是否可以在 REST 服务的 "post()" 方法中提取原始消息正文字符串(即 XML 字符串或 JSON 字符串)?

Is it possible to extract the original message body string (i.e., XML string or JSON string) within the "post()" method of a REST service?

问题:
是否可以在 REST 的 "post()" 方法 中提取原始消息正文字符串(即 XML 字符串或 JSON 字符串)- 服务?

环境

Java 8

WebLogic 12.1.3(带有 jax-rs(2.0,2.5.1) 可部署库)

("request.getInputStream()" 没有产生任何结果......似乎 "read()" 已经被应用 "internally"。此外,"mark()" 或 "reset()" 不支持)

"post()"方法...

    package aaa.bbb.ccc;

    import javax.ejb.Stateless;
    import javax.ws.rs.*;
    import javax.ws.rs.core.MediaType;
    import aaa.bbb.ccc.fieldslist.FieldsList;
    import java.io.*;
    import java.net.URI;
    import javax.ws.rs.core.*;
    import javax.xml.bind.JAXBException;
    import org.apache.commons.io.IOUtils;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;

    @Stateless
    @Path("/fieldsList")
    public class Testws {

        private static final Logger LOG = LogManager.getLogger(Testws.class);

        public Testws() {
        }

        @POST
        @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
        public Response post(@Context UriInfo uriInfo, @Context javax.servlet.http.HttpServletRequest request, FieldsList fieldsList) throws IOException, JAXBException, Exception {
            try {

                //...returns empty string...
                String s = IOUtils.toString(request.getInputStream(), "UTF-8");

                LOG.info("message string from request.getInputStream()=" + s); <==  empty string...
            } catch (Exception e) {
                e.printStackTrace();
            }

            URI uri = UriBuilder.fromUri(uriInfo.getRequestUri()).build();

            Response response = Response.created(uri).build();
            return response;
        }
    }

我尝试使用拦截器(参见 "aroundReadFrom()" 方法)在 InputStream 被 post() 方法使用之前对其进行操作,但是没有效果... -也就是说,在 REST 服务的 post() 方法中,request.getInputStream() 继续不产生任何结果...

"aroundReadFrom()"方法...

    package aaa.bbb.ccc;

    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import javax.ws.rs.WebApplicationException;
    import javax.ws.rs.ext.Provider;
    import javax.ws.rs.ext.ReaderInterceptor;
    import javax.ws.rs.ext.ReaderInterceptorContext;
    import org.apache.commons.io.IOUtils;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;

    @Provider
    public class MyReaderInterceptor implements ReaderInterceptor {

        static final Logger LOG = LogManager.getLogger(MyReaderInterceptor.class);

        @Override
        public Object aroundReadFrom(ReaderInterceptorContext ctx) throws IOException {

            try {
                InputStream is = ctx.getInputStream();
                byte[] content = IOUtils.toByteArray(is);
                is.close();

                ctx.setInputStream(new ByteArrayInputStream(content));            

                return ctx.proceed();            
            } catch (IOException | WebApplicationException e) {
                e.printStackTrace();
            }

            return null;
        }
    }

这里是测试xml消息...:[=​​60=]

    <?xml version="1.0" encoding="UTF-8"?>
    <FieldsList xmlns="http://aaa.bbb.ccc.ws/testws">
        <Fields>
            <FieldA>fieldA_value</FieldA>
            <FieldB>fieldB_value</FieldB>
            <FieldC>fieldC_value</FieldC>
        </Fields>
    </FieldsList>

架构如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema 
        targetNamespace="http://aaa.bbb.ccc.ws/testws"
        attributeFormDefault="unqualified"
        elementFormDefault="qualified"
        xmlns:tw="http://aaa.bbb.ccc.ws/testws"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:complexType name="FieldsType">
            <xs:all>
                <xs:element name="FieldA" type="xs:string" minOccurs="0" />            
                <xs:element name="FieldB" type="xs:string" minOccurs="0" />            
                <xs:element name="FieldC" type="xs:string" minOccurs="0" />                                                 
            </xs:all>
        </xs:complexType>
        <xs:element name="FieldsList">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="Fields" type="tw:FieldsType" minOccurs="0" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:schema>

更新:

在 post() 方法中,我只能使用这种技术重建消息字符串...

    StringWriter sw = new StringWriter();
    JAXBContext.newInstance(FieldsList.class).createMarshaller().marshal(fieldsList, sw);
    System.out.println("posted xml string=" + sw.toString());

...但是,如果相同的数据以 JSON 格式 posted,这将无济于事。为了澄清,它会将 JSON post 消息重建为 XML 字符串而不是原始的 JSON 字符串

同样,我想做的是在 post() 方法

中访问原始 posted XML/JSON 消息字符串

您可以只使用 ReaderInterceptor。这是您可以获得原始数据的地方。您基本上需要从 ReaderInterceptorContext 中获取 InputStream,读取它,然后您需要设置新的 InputStream,因为原始 InputStream 只能读取一次。所以你需要在读取原始流时使用一些缓冲策略

@Override
public Object aroundReadFrom(ReaderInterceptorContext context) {
    InputStream ogStream = context.getInputStream();
    // readStream with some buffer
    // set new stream
    context.setInputStream(bufferedStream);
    return context.proceed();
}

另请参阅:

  • Filters and Interceptors
  • 。此示例用于客户端,但可以很容易地重构为使用服务器端过滤器(即 Container(Request|Response)Filter.

使用请求属性的解决方案 - 测试工作 100%

@Provider
public class ContainerRequestFilterImpl implements ContainerRequestFilter {

    @Context
    private HttpServletRequest request;

    @Override
    public void filter(ContainerRequestContext ctx) throws IOException {

        InputStream is = ctx.getEntityStream();
        byte[] content = IOUtils.toByteArray(is);

        // Store content as Request Attribute
        request.setAttribute("content", new String(content, "UTF-8"));

        ctx.setEntityStream(new ByteArrayInputStream(content));
    }
}


AND

@POST
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response post(@Context UriInfo uriInfo, @Context HttpServletRequest request, FieldsList fieldsList) throws IOException, JAXBException, Exception {
    try {
        String s = request.getAttribute("postContent");
        LOG.info("Post content: " + s);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

我认为您可以使用 ReaderInterceptor 而不是 ContainerRequestFilter。它也应该有效。