Ich habe eine RESTFul-API, die JSON im Request/Response-Body verbraucht/zurückgibt. Wenn der Client ungültige Daten (gültiger JSON, aber ungültige Werte für die Felder) sendet, möchte ich eine JSON-Struktur (sowie den relevanten Code 400+) zurückgeben können.Wie gebe ich in Java sinnvolle JSON-formatierte Fehler aus der Reseasy-Validierung zurück?
Diese Struktur würde es dann dem Frontend ermöglichen, die Fehler pro Feld zu analysieren und die Fehler neben den Eingabefeldern darzustellen.
z. ideal Ausgang:
{
"errors":{
"name":["invalid chars","too long","etc"]
"otherfield":["etc"]
}
}
Ich bin mit Resteasy für die API und die Verletzung Ausnahmen verwendet es ist ziemlich einfach, um es JSON Fehler zu machen:
@Provider
@Component
public class ValidationExceptionHandler implements ExceptionMapper<ResteasyViolationException> {
public Response toResponse(ResteasyViolationException exception) {
Multimap<String,String> errors = ArrayListMultimap.create();
Consumer<ResteasyConstraintViolation> consumer = (violation) -> {
errors.put(violation.getPath(), violation.getMessage());
};
exception.getParameterViolations().forEach(consumer);
Map<String, Map<String, Collection<String>>> top = new HashMap<>();
top.put("errors", errors.asMap());
return Response.status(Status.BAD_REQUEST).entity(top)
.build();
}
}
jedoch die Fehlerpfade (violation.getPath()
) sind geistiges Eigentum -centric statt XmlElement-Name-centric.
z. die oben genannten Ausgänge:
{
"errors":{"createCampaign.arg1.name":["invalid chars","etc"]}
}
habe ich versucht, den Index zurück aus dem letzten Punkt Strippen „namen“ zu bekommen, aber es gibt auch andere Probleme mit diesem Hack.
z. wenn meine Eigenschaft „Name“ ist nicht „name“ es funktioniert nicht:
@XmlElement(name="name")
@NotNull
private String somethingelse;
„somethingelse“ wird an den Client zurückgegeben werden, aber sie Kunde hat keine Ahnung, was das ist:
{
"errors":{"somethingelse":["cannot be null"]}
}
Der Client möchte "name", da das Feld beim Senden aufgerufen wurde.
Meine Ressource:
package com.foo.api;
import org.springframework.stereotype.Service;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.foo.dto.CarDTO;
@Service
@Path("/car")
public class CarResource {
@POST
@Produces({MediaType.APPLICATION_JSON})
@Consumes(MediaType.APPLICATION_JSON)
public CarDTO create(
@Valid CarDTO car
) {
//do some persistence
return car;
}
}
Beispiel dto:
package com.foo.dto;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Min;
import javax.validation.constraints.Max;
import javax.xml.bind.annotation.XmlElement;
public class CarDTO {
@Min(1)
@Max(10)
@NotNull
@XmlElement(name="gears")
private int cogs;
}