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
@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:
@Valid
annotation from controller method. 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.