简体   繁体   中英

Java Spring RestTemplate POST issue, while Postman works

I'm quite new to Spring and I'm trying to use it to save Mandrill webhooks events. After creating all the required validators I've discovered that it's sending its events using application/x-www-form-urlencoded header, which was preventing all the validators to work.

After that, I've decided to handle Mandrill webhooks remapping its request as a valid JSON object, with its header Content-Type: application/json .

Searching on SO, I've found so many ways to send a POST request from Spring to itself, and after a few tries, I've started using RestTemplate , like this

Webhook Controller

@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public void remap(@RequestParam String mandrill_events) throws IOException {
    JsonNodeFactory factory = JsonNodeFactory.instance;
    ArrayNode eventsNode = factory.arrayNode();
    ObjectNode eventNode = factory.objectNode();
    ObjectMapper mapper = new ObjectMapper();
    JsonNode node = mapper.readTree(mandrill_events);
    if (node.isArray()) {
        for (int i = 0; i < node.size(); i++) {
            eventsNode.add(node.get(i));
        }
    }
    eventNode.set("mandrill_events", eventsNode);
    String webhookStoreURL = this.globalConfig.APIUrl + "/webhook/store";
    RestTemplate restTemplate = new RestTemplate();
    try {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        HttpEntity<String> httpEntity = new HttpEntity<>(eventNode.toString(), httpHeaders);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(webhookStoreURL, httpEntity, String.class);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@RequestMapping(path = "/store", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public void store(@RequestBody @Valid MandrillEventList eventList) throws StorageException, NotFoundException, SQLException {
    System.out.println("Saving " + eventList.getMandrillEvents().size() + " events");
    //this.eventService.storeEvents(eventList);
}

The problem is, when I send the RestTemplate request, the only thing I get it's a 400 null error from the catch block.

So this is what I've tried so far:

  • Removing @Valid annotation from controller method. Didn't work
  • Removing all the validators from the global one. Didn't work

Seemed like it was trying to validate request body, even without annotations, so I've tried those two as well

  • Copying the request body from debug and testing it on Postman. Works (shows validation errors correctly)

  • Using the Mandrill test webhook. Works (Spring and Postman)

I have no idea about where to look

I came out with a solution. Instead of re-sending the API request to himself, I've done this

@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public void store(@RequestParam String mandrill_events) throws IOException {
    JsonNodeFactory factory = JsonNodeFactory.instance;
    ArrayNode eventsNode = factory.arrayNode();
    ObjectNode eventNode = factory.objectNode();
    ObjectMapper mapper = new ObjectMapper();
    JsonNode node = mapper.readTree(mandrill_events);
    if (node.isArray()) {
        for (int i = 0; i < node.size(); i++) {
            eventsNode.add(node.get(i));
        }
    }
    eventNode.set("mandrill_events", eventsNode);
    try {
        MandrillEventList eventList = mapper.readValue(eventNode.toString(), MandrillEventList.class);
        logger.log(Level.FINE, "Webhook has " + eventList.getMandrillEvents().size() + " events to save");
        this.eventService.storeEvents(eventList);
    } catch (Exception e) {
        e.printStackTrace();
        logger.log(Level.SEVERE, "Unable to setup mandrill event list " + e.getMessage());
        throw new IOException("Unable to setup mandrill event list");
    }
}

I rebuild the JSON as Controller expected it then I map it as the original MandrillEventList object with Jackson ObjectMapper . It gave me some problems, for example when he didn't recognize properties which weren't inside class description, so I've added the @JsonIgnoreProperty annotation like this:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MandrillEvent {
 [...]
}

Everything works right now

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