【问题标题】:Get XML in plain text以纯文本形式获取 XML
【发布时间】:2019-04-26 07:15:52
【问题描述】:

我有这个 Spring Rest API 端点:

@PostMapping(value = "/v1/", consumes = { MediaType.APPLICATION_XML_VALUE,
            MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_XML_VALUE,
                    MediaType.APPLICATION_JSON_VALUE })
    public PaymentResponse handleMessage(@RequestBody PaymentTransaction transaction, HttpServletRequest request) throws Exception {

    // get here plain XML  

}

XML 模型。

@XmlRootElement(name = "payment_transaction")
@XmlAccessorType(XmlAccessType.FIELD)
public class PaymentTransaction {
    public enum Response {
        failed_response, successful_response
    }

    @XmlElement(name = "transaction_type")
    public String transactionType;
    .........
}

如何以纯 XML 文本获取 XML 请求?

我也尝试过使用 Spring 拦截器: 我试过这段代码:

@SpringBootApplication
@EntityScan("org.plugin.entity")
public class Application extends SpringBootServletInitializer implements WebMvcConfigurer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    ........

    @Bean
    public RestTemplate rsestTemplate() {
        List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        RestTemplate restTemplate = new RestTemplate(
                new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    } 
}

记录组件:

@Component
public class RestTemplateHeaderModifierInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
            throws IOException {

        StringBuilder sb = new StringBuilder();
        sb.append("[ ");
        for (byte b : body) {
            sb.append(String.format("0x%02X ", b));
        }
        sb.append("]");

        System.out.println("!!!!!!!!!!!!!!!");
        System.out.println(sb.toString());      

        ClientHttpResponse response = execution.execute(request, body);

        InputStream inputStream = response.getBody();

        String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);

        System.out.println("!!!!!!!!!!!!!!!");
        System.out.println(result);

        return response;
    }
}

但是没有任何东西打印到控制台中。知道我哪里错了吗?可能这个组件没有注册?

【问题讨论】:

  • 我认为你的拦截器工作不正常。无论如何,我认为使用拦截器进行日志记录有点过头了。你确定这是你想要的吗?
  • 您真的希望请求是纯文本还是只是想转换或以其他方式转换 xml?如果您想以纯文本形式接收,您将无法使用 XML 或使用任何约定而不是使用 XML 的配置优势,而无需将其作为文本接收,然后将其转换为 XML,然后再转换回文本。我只是想澄清一下。你也可以提供一个样本请求
  • @ChrisMaggiulli 我想记录一切。不仅是 XML,还有请求尝试。你能给出一些如何实施的建议吗?
  • 你看过this older的答案吗?看起来它回答了几乎相同的问题。
  • 你忘了给interceptors添加拦截器@list

标签: java spring rest spring-restcontroller spring-rest


【解决方案1】:

您已创建 List&lt;ClientHttpRequestInterceptor&gt; interceptors = new ArrayList&lt;&gt;();,但未向其中添加 RestTemplateHeaderModifierInterceptor 对象。

您可以在Application 中自动连接,如下所示:

@Autowired 
ClientHttpRequestInterceptor clientHttpRequestInterceptor;

interceptors.add(clientHttpRequestInterceptor);

代码如下:

class Application {
...
@Autowired 
ClientHttpRequestInterceptor clientHttpRequestInterceptor;
@Bean
    public RestTemplate rsestTemplate() {
        List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        RestTemplate restTemplate = new RestTemplate(
                new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
interceptors.add(clientHttpRequestInterceptor);
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    } 
 ...
}

希望对你有帮助

【讨论】:

    【解决方案2】:

    我们在生产环境中使用spring-mvc-logger 已经有一段时间了。它被编写为 servlet 过滤器,因此可以作为独立包装器添加到 MVC 端点。

    我们的设置几乎与那里的 readme.md 中描述的完全一样,尽管我们将 &lt;filter-mapping&gt; 下的 &lt;url-pattern&gt; 限制为仅有用的端点。

    即使它不完全是您所追求的,那里的代码库也是一个很好的小例子。特别注意过滤器中需要的请求/响应包装。 (这是为了避免IllegalStateException: getReader(), getInputStream() already called,否则如果getReader() 被调用两次就会发生)。

    【讨论】:

      【解决方案3】:

      为了让您的控制器以纯 xml 字符串形式接收请求正文,您只需将 @RequestBody 参数类型更改为字符串:

      @PostMapping(value = "/v1/", consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
      public PaymentResponse handleMessage(@RequestBody String xmlOrJson, HttpServletRequest request) throws Exception {
          ...
      

      使用上面的映射,如果客户端已经提交了 xml,你会看到原始的 XML。否则,如果客户端已提交 json,您将看到原始 JSON。确保检查请求的“Content-Type”标头以了解您正在处理的类型。

      https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-requestbody

      【讨论】:

      • 是的,但我还需要解析的对象和 XML 作为字符串。我都试过了,但都不行。
      【解决方案4】:

      我们遇到了同样的问题,并在生产中使用了这个解决方案。这不依赖于框架(在我的书中总是一个优势)并且很简单。

      只需使用它而不将其指定为 XML。然后阅读请求行并通过\n 加入它们,如果你想在你的xml 中有新的行。如果没有,请通过"" 或任何您喜欢的方式加入他们。这假设您使用的是javax.servlet.http.HttpServletRequest

      例子:

      @PostMapping(value = "/v1")
          public PaymentResponse handleMessage(HttpServletRequest request) throws Exception {
      
          final InputStream xml = request.getInputStream();
          final String xmlString = new BufferedReader(new InputStreamReader(xml))
                .lines()
                .collect(Collectors.joining("\n"));
         // do whatever you please with it
      
      }
      

      你有一个纯 xml 字符串。

      【讨论】:

      • 我得到原因:java.io.IOException: UT010029: Stream is closed
      • 你能分享 xml 字符串的结尾部分吗?
      • 另外,该堆栈跟踪肯定还有其他内容,对吧?
      【解决方案5】:

      除非我遗漏了一些东西,否则从 HttpServletRequest 获取它不应该像下面这样容易吗。我认为不需要使用拦截器等。

      @PostMapping(value = "/v1/", consumes = { MediaType.APPLICATION_XML_VALUE,
                  MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_XML_VALUE,
                          MediaType.APPLICATION_JSON_VALUE })
          public PaymentResponse handleMessage(HttpServletRequest request) throws Exception {
      
          String str, wholeXML = "";
          try {
              BufferedReader br = request.getReader();
              while ((str = br.readLine()) != null) {
                  wholeXML += str;
              }
          System.out.println(wholeXML);
          //Here goes comment question, to convert it into PaymentTransaction
         JAXBContext jaxbContext = JAXBContext.newInstance(PaymentTransaction.class);
          Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
      
          StringReader reader = new StringReader(wholeXML);
          PaymentTransaction paymentTransaction = (PaymentTransaction) unmarshaller.unmarshal(reader);
      }
      

      【讨论】:

      • 但我得到 java.lang.IllegalStateException: UT010004: 无法调用 getReader(),getInputStream() 已经调用
      • @PeterPenzov 对不起,我的错,我刚刚测试过,你需要从签名中删除@RequestBody PaymentTransaction transaction
      • 是的,但我还想获得 XML 对象 PaymentTransaction。在这里,我需要获取 XML 对象以及单独的 String 变量中的请求内容。可能我需要使用拦截器?
      • 从 XML 中,您也可以得到 PaymentTransaction,但这里 Spring 不会直接帮助您进行注释。即使您想要,我也可以向您展示如何将 XML 转换为 PaymentTransaction 对象?
      • 好的,你能告诉我如何将 PaymentTransaction 对象转换为 XML 吗?
      猜你喜欢
      • 2015-12-08
      • 1970-01-01
      • 2016-03-03
      • 1970-01-01
      • 1970-01-01
      • 2012-08-13
      • 2021-03-21
      • 1970-01-01
      • 2020-12-19
      相关资源
      最近更新 更多