简体   繁体   中英

Get XML in plain text

I have this endpoint for 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 model.

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

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

How I can get the XML request in plain XML text?

I also tried with Spring interceptor: I tried this code:

@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 for logging:

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

But nothing is printed into the console. Any idea where I'm wrong? Probably this component is not registered?

Shouldn't it be easy like below to get it from HttpServletRequest, unless I'm missing something. I don't think there is need to use interceptor etc.

@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);
}

We had the same issue and use this solution in production. Which is not framework dependent (always an upside in my book) and simple.

Just consume it without specifying it as an XML. Then read the request lines and join them by \\n if you want to have new lines in your xml. If not, join them by "" or whatever you please. This presumes you are using the javax.servlet.http.HttpServletRequest

Example:

@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

}

And you have an plain xml string.

For your controller to receive the request body as a plain xml string, you need only change the @RequestBody parameter type to String:

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

With the above mapping, if the client has submitted xml, you'll see the raw XML. Otherwise, if the client has submitted json, you'll see the raw JSON. Make sure you check the request's "Content-Type" header to know which type you're dealing with.

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

We've been using the spring-mvc-logger in production for quite a while. It's written as a servlet filter, so can be added as an independent wrapper to the MVC endpoint.

Our set up is almost exactly like described on the readme.md there, though we restrict the <url-pattern> under the <filter-mapping> to just the useful endpoints.

Even if it's not exactly what you're after, the codebase there makes quite a nice small example. In particular note the request/response wrapping that is needed in the filter. (This is to avoid the IllegalStateException: getReader(), getInputStream() already called that would otherwise happen if getReader() were called twice).

You have created List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); but did not add the RestTemplateHeaderModifierInterceptor object to it.

You can autowire in the same in Application like below:

@Autowired 
ClientHttpRequestInterceptor clientHttpRequestInterceptor;

and

interceptors.add(clientHttpRequestInterceptor);

The code looks like below:

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

Hope it helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM