Have a pojo which is suppled via a rest call. One of the fields of the pojo is a JSON. The JSON can be any valid JSON. It is a Run Time value supplied by a couple of different inputs. The goal is to throw an exception if the JSON field is not a valid JSON
Have tried:
In POJO
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
private JsonNode json;
Have also tried using
#JsonRawValue
However when I supply anything it picks it up as a node of some type (TestNode, IntNode) etc.
anyway to do the validation without introduction of extra code? If not how to check at the controller when the object comes in?
One way to do this is use org.springframework.validation.Validator
You can annotate your controller with
#PostMapping("/abc")
public Test test((#Validated(YourValidator.class)
#RequestBody YourPojo pojo)) {
return test;
}
Now you can write your own validation
public class YourValidator implements org.springframework.validation.Validator {
#Override
public void validate(Object target, Errors errors) {
YourPojo pojo = (YourPojo) target;
boolean validJson =false;
try {
final ObjectMapper mapper = new ObjectMapper(); //jackson library
String jsonInString = pojo.getJsonField() ;
mapper.readTree(jsonInString);
validJson= true;
} catch (IOException e) {
validJson= false;
}
errors.rejectValue("jsonField", "notValid");
}
}
Related
When an item that doesn't exist in my web app is invoked through an URL, Spring responds with a JSON with data like (timestand, status, error, message, path). So, I need to change the structure of this JSON, specificly I need to remove path.
How can I do it?
Where should I implement the customization of the exception in my project?
Best regards to everyone!
Json response to modify
It's pretty easy in Spring MVC applications to handle errors by their types using the #ContollerAdvice class.
You could define your own handler for the exceptions you get on a method calls.
E.g.:
#ControllerAdvice
public class ErrorHandler {
#ExceptionHandler(value = ExceptionToHandle.class)
#ResponseBody
public YourResponse handle(ExceptionToHandle ex) {
return new YourResponse(ex.getMessage());
}
}
Here YourResponse is just a POJO, that could have any structure your want to be presented at the client.
The #ExceptionHandler specifies what types of errors will be handled in the method (including more specific types).
The #ResponseBody says that your returned value will be presented in the JSON format in your response.
You may try something like that:
#RestController
#RequestMapping("/")
public class TestController {
#GetMapping("/exception")
void getException() {
throw new MyCustomException("Something went wrong!");
}
class MyCustomException extends RuntimeException {
MyCustomException(String message) {
super(message);
}
}
class CustomError {
private String message;
private Integer code;
CustomError(String message, Integer code) {
this.message = message;
this.code = code;
}
public String getMessage() {
return message;
}
public Integer getCode() {
return code;
}
}
#ExceptionHandler(MyCustomException.class)
public CustomError handleMyCustomException(Exception ex) {
return new CustomError("Oops! " + ex.getMessage(), HttpStatus.BAD_REQUEST.value());
}
}
Fast and simple, you can just make your own exception and your own error object (which is later turned to json).
If you ask where to put such a thing... Well, you can make a separate class for the exception (and an exception package also), and put a small #ExceptionHandler method inside your controller. If you don't want it to be in the same class, you may delegate it to separate class also; for further and in-depth reading, look up for annotation like #ControllerAdvice.
I am doing validation for request object in spring boot rest. I have to validate data type of request. The request has multiple boolean values and trying to validate if string in passed for boolean data type.
I have handling HttpMessageNotReadableException in my ControllerAdvice class and sending list of error message. But in my response only first field is throwing exception. If clue ,please help.
#Vishnu Dubey use this .....
#RestControllerAdvice
public class ServiceControllerAdvice {
private static final Logger log = LoggerFactory.getLogger(ServiceControllerAdvice.class);
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ServiceResponse<?> constraintViolationException(final ConstraintViolationException ex) {
log.error("Validation failed", ex);
final ServiceResponse<?> response = new ServiceResponse<>(-1);
final Error error = new Error();
error.setCode("PS01");
error.setContext(ex);
error.setMessage(ex.getMessage());
response.setError(error);
return response;
}
}
I'm using Spring to create an API, but I'm having some trouble introducing custom error reporting on (a part of) the validation of the request body.
When parsing/validation errors occur, I want to give a custom response back to the user.
This works well for fields annotated with #Valid along with validators like #javax.validation.constraints.NotNull by using a custom ResponseEntityExceptionHandler annotated with #ControllerAdvice.
It does not work however if an Exception is thrown while parsing the request body (before the validations even run). In that case I get an html error page with status 500 (Server Error)
How can I make sure the exceptions during parsing lead to the same kind of response as the (custom) one I return for validation failures?
My endpoint's code looks like this:
#RequestMapping(value= "/endpoint"
produces = { "application/json" },
consumes = { "application/json" },
method = RequestMethod.POST)
default ResponseEntity<Object> postSomething(#Valid #RequestBody MyRequestBody requestData){
// ...
}
MyRequestBody class looks like this:
#Validated
public class MyRequestData {
#JsonProperty("stringValue")
private String stringValue = null;
#NotNull
#Valid
public String getStringValue() {
return stringValue;
}
// ...
public enum EnumValueEnum {
VALUE_1("value 1"),
VALUE_1("value 2");
private String value;
EnumValueEnum(String value) {
this.value = value;
}
#Override
#JsonValue
public String toString() {
return String.valueOf(value);
}
#JsonCreator
public static EnumValueEnum fromValue(String text) {
if(text == null){
return null;
}
for (EnumValueEnum b : EnumValueEnum.values()){
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
throw new HttpMessageNotReadableException("EnumValueEnum \"" + text + "\" does not exist");
}
}
#JsonProperty("enumValue")
private EnumValueEnum enumValue = null;
}
The custom validation error handling (and reporting) looks like this:
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class MyValidationHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// return status(BAD_REQUEST).body(new ValidationResponse(ex.getBindingResult().getFieldErrors()));
}
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// return status(BAD_REQUEST).body(new ValidationResponse((JsonMappingException) ex.getCause()));
}
}
In this code, if a user sends a request with an enum value that doesn't exist, an HttpMessageNotReadableException is thrown. I would like to catch that somewhere and replace it with a custom response that is consistent with the other exception handling I do. Where/How can I do that?
I found a solution to my own problem.
You can actually use Spring MVC's normal exception handling:
Annotating a method with #ExceptionHandler will make Spring try to use it for exception handling for the exception type specified (in the annotation's value field or the method's argument). This method can be placed in the controller or even in the ResponseEntityExceptionHandler I use for the other validation response handling.
#ExceptionHandler
public ResponseEntity handle(HttpMessageConversionException e){
// return status(BAD_REQUEST).body(new ValidationResponse((JsonMappingException) e.getCause()));
}
Mind which type of exception you handle:
The catch here was that the exception thrown while parsing is wrapped in (some subtype of) a JsonMappingException which in turn is wrapped again in a HttpMessageConversionException.
e instanceof HttpMessageConversionException
e.getCause() instanceof JsonMappingException
e.getCause().getCause() // == your original exception
The #ExceptionHandler should therefor accept HttpMessageConversionException instead of the originally thrown exception (which in my case was HttpMessageNotReadableException)
It will not work if you write an #ExceptionHandler that only accepts your original Exception!
While implementing a global exception handler in Spring, I noticed that in case of a not recognized Accept header, Spring would throw it's own internal error. What I need is to return a custom JSON error structure instead. Works fine for application specific exceptions and totally fails for Spring HttpMediaTypeNotAcceptableException.
This code tells me "Failed to invoke #ExceptionHandler method: public java.util.Map RestExceptionHandler.springMalformedAcceptHeaderException()" when I try to request a page with incorrect Accept header. Any other way to return custom JSON for spring internal exceptions?
#ControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(value = HttpMediaTypeNotAcceptableException.class)
#ResponseBody
public Map<String, String> springMalformedAcceptHeaderException() {
Map<String, String> test = new HashMap<String, String>();
test.put("test", "test");
return test;
}
}
Eventually figured that the only way is to do the json mapping manually.
#ExceptionHandler(value = HttpMediaTypeNotAcceptableException.class)
#ResponseBody
public String springMalformedAcceptHeaderException(HttpServletResponse response) {
// populate errorObj, set response headers, etc
ObjectWriter jsonWriter = new ObjectMapper().writer();
try {
return jsonWriter.writeValueAsString(errorObj);
} catch(Exception e){}
return "Whatever";
}
TL;DR - Is there a way to throw an error from a registered type converter during the MVC databinding phase such that it will return a response with a specific HTTP status code? I.e. if my converter can't find an object from the conversion source, can I return a 404?
I have a POJO:
public class Goofball {
private String id = "new";
// others
public String getName () { ... }
public void setName (String name) { ... }
}
and am using a StringToGoofballConverter to create an empty object when "new".equals(id) or try to load a Goofball from the database if it exists:
public Goofball convert(String idOrNew) {
Goofball result = null;
log.debug("Trying to convert " + idOrNew + " to Goofball");
if ("new".equalsIgnoreCase(idOrNew))
{
result = new Goofball ();
result.setId("new");
}
else
{
try
{
result = this.repository.findOne(idOrNew);
}
catch (Throwable ex)
{
log.error (ex);
}
if (result == null)
{
throw new GoofballNotFoundException(idOrNew);
}
}
return result;
}
That converter is used by spring when the request matches this endpoint:
#RequestMapping(value = "/admin/goofballs/{goofball}", method=RequestMethod.POST)
public String createOrEditGoofball (#ModelAttribute("goofball") #Valid Goofball object, BindingResult result, Model model) {
// ... handle the post and save the goofball if there were no binding errors, then return the template string name
}
This all works quite well insofar as GET requests to /admin/goofballs/new and /admin/goofballs/1234 work smoothly in the controller for both creating new objects and editing existing ones. The hitch is that if I issue a request with a bogus id, one that isn't new and also doesn't exist in the database I want to return a 404. Currently the Converter is throwing a custom exception:
#ResponseStatus(value= HttpStatus.NOT_FOUND, reason="Goofball Not Found") //404
public class GoofballNotFoundException extends RuntimeException {
private static final long serialVersionUID = 422445187706673678L;
public GoofballNotFoundException(String id){
super("GoofballNotFoundException with id=" + id);
}
}
but I started with a simple IllegalArgumentException as recommended in the Spring docs. In either case, the result is that Spring is returning a response with an HTTP status of 400.
This makes me think I'm misusing the Converter interface but that approach appears to be recommended by the #ModelAttribute docs.
So, again the question: is there a way to throw an error from a registered type converter during the databinding phase such that it will return a response with a specific HTTP status code?
Answering my own question:
Change StringToGoofballConverter to simply return null for the unfound entity instead of throwing IllegalArgumentException or a custom exception. The #Controller method will then be given a Goofball object that has a null id (e.g. the id is not "new" nor the path element value). At that point I can throw a GoofballNotFoundException or any other #ResponseStatus exception from there, within the controller method to affect the response status code.