How to handle GraphQL query validation error in Spring Boot - java

I have a simple Spring Boot project using spring-boot-starter-graphql. This project has one controller that accepts one argument.
#Controller
public class HelloNameController {
#QueryMapping
public String hello(#Argument String name) {
return "Hello " + name;
}
}
This argument is required.
Graphql schema
type Query {
hello (name : String!) : String
}
When I call this API in the Postman and do not pass this argument the app returns an error. I want to override the message of this error message, but I can't find a way to do it.
In the official documentation, it says to implement DataFetcherExceptionResolverAdapter and I've implemented it as a bean
#Configuration
public class GraphQLConfig {
#Bean
public DataFetcherExceptionResolver exceptionResolver() {
return DataFetcherExceptionResolverAdapter.from((ex, env) -> {
if (ex instanceof CoercingParseValueException) {
return GraphqlErrorBuilder.newError(env).message("CoercingParseValueException")
.errorType(ErrorType.ExecutionAborted).build();
}
if (ex instanceof CoercingSerializeException) {
return GraphqlErrorBuilder.newError(env).message("CoercingSerializeException")
.errorType(ErrorType.ExecutionAborted).build();
} else {
return null;
}
});
}
}
The problem is that the error never gets to this point. How do I catch this type of error and override the message?

I've asked a similar question on a GitHub. Responses from graphql-java project (#2866) and spring-graphql project (#415) were similar. To summarise at the time of writing it is not possible.
Then I've created a "workaround":
First, create a custom exception class that implements GraphQLError.
import graphql.GraphQLError;
import graphql.language.SourceLocation;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.http.HttpStatus;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
#Getter
#NoArgsConstructor
public class BadRequestException extends RuntimeException implements GraphQLError {
private HttpStatus status = HttpStatus.BAD_REQUEST;
private String message = "Resource not found";
// Below code used for GraphQL only
private List<SourceLocation> locations;
public BadRequestException(String message, List<SourceLocation> locations) {
this.message = message;
this.locations = locations;
}
#Override
public Map<String, Object> getExtensions() {
Map<String, Object> customAttributes = new LinkedHashMap<>();
customAttributes.put("errorCode", this.status.value());
return customAttributes;
}
#Override
public List<SourceLocation> getLocations() {
return locations;
}
#Override
public ErrorType getErrorType() {
return ErrorType.BAD_REQUEST;
}
#Override
public Map<String, Object> toSpecification() {
return GraphQLError.super.toSpecification();
}
}
Second, create an interceptor class that implements WebGraphQlInterceptor and annotate it as #Component, so Spring can create it as a bean. Inside this class implement logic to catch the needed error and convert it to the exception class created before
import graphql.ErrorClassification;
import graphql.ErrorType;
import graphql.GraphQLError;
import graphql.validation.ValidationErrorType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.graphql.ResponseError;
import org.springframework.graphql.server.WebGraphQlInterceptor;
import org.springframework.graphql.server.WebGraphQlRequest;
import org.springframework.graphql.server.WebGraphQlResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.stream.Collectors;
#Slf4j
#Component
public class ErrorInterceptor implements WebGraphQlInterceptor {
#Override
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
return chain.next(request)
.map(response -> {
log.info("[ErrorInterceptor] Intercepting response... ");
List<GraphQLError> graphQLErrors = response.getErrors().stream()
.filter(error -> ErrorType.ValidationError.equals(error.getErrorType()))
.map(this::resolveException)
.collect(Collectors.toList());
if (!graphQLErrors.isEmpty()) {
log.info("[ErrorInterceptor] Found invalid syntax error! Overriding the message.");
return response.transform(builder -> builder.errors(graphQLErrors));
}
return response;
});
}
private GraphQLError resolveException(ResponseError responseError) {
ErrorClassification errorType = responseError.getErrorType();
if (ErrorType.ValidationError.equals(errorType)) {
String message = responseError.getMessage();
log.info("[ErrorInterceptor] Returning invalid field error ");
if (ValidationErrorType.NullValueForNonNullArgument.equals(
extractValidationErrorFromErrorMessage(responseError.getMessage()))) {
String errorMessage =
"Field " + StringUtils.substringBetween(message, "argument ", " #") + " cannot be null";
return new BadRequestException(errorMessage, responseError.getLocations());
}
}
log.info("[ErrorInterceptor] Returning unknown query validation error ");
return new BadRequestException("Unknown error", responseError.getLocations());
}
private ValidationErrorType extractValidationErrorFromErrorMessage(String message) {
return ValidationErrorType.valueOf(StringUtils.substringBetween(message, "type ", ":"));
}
}
The only problem with this approach is that all needed information like a type of an error, the field that causes the error, etc. is embedded in the native error message. So to extract the needed parameters we have to parse the string message.

Related

Making attribute in json request object not null

I'm building a Spring application which has #RestController, like:
#RestController
#RequestMapping(value = "/master")
public class MyController {
#Autowired
private MyService service;
#PostMapping("/call")
public ResponseEntity<Boolean> apiCall(#RequestBody MyDTO myDto) { ;
return new ResponseEntity<Boolean>(service.apiCall(myDto), OK);
}
}
And a request object:
public class MyDTO {
#JsonProperty("emp_number")
private long empNumber;
#JsonProperty("office_id")
private long officeId;
// ....constructors, etc.
}
In request json I want officeId to be not null.
So far I've tried marking the officeId field as:
#com.fasterxml.jackson.databind.annotation.JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
#JsonProperty(required = true)
#javax.validation.constraints.NotNull
But in the request json, even if I miss office_id, it is not throwing any error.
What am I missing?
It could be that in your case you are deserializing request, and missing primitive properties which are referenced by constructor are assigned a default value
java defaults
You could try to use corresponding Long wrapper object for MyDTO instead of primitives or maybe deserialization feature FAIL_ON_NULL_FOR_PRIMITIVES
You have to change the type from primitive to wrapper otherwise the default value of 0 will be considered and validation will pass.
Annotate MyDTO with #Valid annotation.
When Spring Boot finds an argument annotated with #Valid, it automatically bootstraps the default JSR 380 implementation — Hibernate Validator — and validates the argument.
from here
Please add following dependency to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
PS: i have made minor adjustments to response structure to see the error in response
Entire code is as follows:
package com.example.spring.java.springjavasamples;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;
#SpringBootApplication
public class SpringJavaSamplesApplication {
public static void main(String[] args) {
SpringApplication.run(SpringJavaSamplesApplication.class, args);
}
}
#RestController
#RequestMapping(value = "/master")
class MyController {
private final MyService service;
public MyController(MyService service) {
this.service = service;
}
#PostMapping("/call")
public ResponseEntity<Map<String, Object>> apiCall(#Valid #RequestBody MyDTO myDto, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
Map<String, String> errors = new HashMap<>();
bindingResult.getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return prepareResponse("error", errors, HttpStatus.BAD_REQUEST);
} else {
return prepareResponse("data", service.apiCall(myDto), HttpStatus.OK);
}
}
private ResponseEntity<Map<String, Object>> prepareResponse(String key, Object data, HttpStatus status) {
Map<String, Object> map = new HashMap<>();
map.put(key, data);
return new ResponseEntity<>(map, status);
}
}
class MyDTO {
#NotNull(message = "Employee number cannot be null")
#JsonProperty("emp_number")
private Long empNumber;
#NotNull(message = "Office Id cannot be null")
#JsonProperty("office_id")
private Long officeId;
#Override
public String toString() {
return "MyDTO{" +
"empNumber=" + empNumber +
", officeId=" + officeId +
'}';
}
}
#Service
class MyService {
public String apiCall(MyDTO myDto) {
System.out.println("all valid: " + myDto);
return myDto.toString();
}
}

#ControllerAdvice annotated class is not catching the exception thrown in the service layer

I'm trying to centralize the error handling in my spring boot app. Currently i'm only handling one potential exception (NoSuchElementException), this is the controller advice:
import java.util.NoSuchElementException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
#ControllerAdvice
public class ExceptionController {
#ExceptionHandler(NoSuchElementException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public DispatchError dispatchNotFound(NoSuchElementException exception) {
System.out.println("asdasdasd");
return new DispatchError(exception.getMessage());
}
}
And here's the service which throws the exceptions:
import java.util.List;
import com.deliveryman.deliverymanapi.model.entities.Dispatch;
import com.deliveryman.deliverymanapi.model.repositories.DispatchRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class DaoService {
#Autowired
DispatchRepository dispatchRepo;
public Dispatch findByShipmentNumber(long shipmentNumber) {
return dispatchRepo.findById(shipmentNumber).orElseThrow();
}
public List<Dispatch> findByUser(String user, String status) {
if(status == null) {
return dispatchRepo.findByOriginator(user).orElseThrow();
} else {
return dispatchRepo.findByOriginatorAndStatus(user, status).orElseThrow();
}
}
public Dispatch createDispatch(Dispatch dispatch) { //TODO parameter null check exception
return dispatchRepo.save(dispatch);
}
}
The problem is that once I send a request for an inexistent resource, the json message shown is the spring's default one. It should be my custom json error message (DispatchError).
Now, this is fixed by adding a #ResponseBody to the exception handler method but the thing is that I was using an old code of mine as reference, which works as expected without the #ResponseBody annotation.
Can someone explain me why this is happening?
Either annotate your controller advice class with #ResponseBody
#ControllerAdvice
#ResponseBody
public class ExceptionController {
...
or replace #ControllerAdvice with #RestControllerAdvice.
Tested and verified on my computer with source from your controller advice.
From source for #RestControllerAdvice
#ControllerAdvice
#ResponseBody
public #interface RestControllerAdvice {
...
Hence, #RestControllerAdvice is shorthand for
#ControllerAdvice
#ResponseBody
From source doc for #ResponseBody
Annotation that indicates a method return value should be bound to the
web response body. Supported for annotated handler methods.
Alternative using #ControllerAdvice only:
#ControllerAdvice
public class ExceptionHandlerAdvice {
#ExceptionHandler(NoSuchElementException.class)
public ResponseEntity<DispatchError> dispatchNotFound(NoSuchElementException exception) {
return new ResponseEntity<>(new DispatchError(exception.getMessage()), HttpStatus.NOT_FOUND);
}
}
I do have a theory on what's going on in your old app. With the advice from your question, and the error handler below, I can create a behaviour where the DispatchError instance appears to be returned by advice (advice is executed), but is actually returned by error controller.
package no.mycompany.myapp.error;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
#RestController
#RequiredArgsConstructor
public class ErrorHandler implements ErrorController {
private static final String ERROR_PATH = "/error";
private final ErrorAttributes errorAttributes;
#RequestMapping(ERROR_PATH)
DispatchError handleError(WebRequest webRequest) {
var attrs = errorAttributes.getErrorAttributes(webRequest, ErrorAttributeOptions.of(ErrorAttributeOptions.Include.MESSAGE));
return new DispatchError((String) attrs.get("message"));
}
#Override
public String getErrorPath() {
return ERROR_PATH;
}
}
Putting an implementation of ErrorController into classpath, replaces Spring's BasicErrorController.
When reinforcing #RestControllerAdvice, error controller is no longer in effect for NoSuchElementException.
In most cases, an ErrorController implementation that handles all errors, in combination with advice exception handlers for more complex exceptions like MethodArgumentNotValidException, should be sufficient. This will require a generic error DTO like this
package no.mycompany.myapp.error;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.Map;
#Data
#NoArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiError {
private long timestamp = new Date().getTime();
private int status;
private String message;
private String url;
private Map<String, String> validationErrors;
public ApiError(int status, String message, String url) {
this.status = status;
this.message = message;
this.url = url;
}
public ApiError(int status, String message, String url, Map<String, String> validationErrors) {
this(status, message, url);
this.validationErrors = validationErrors;
}
}
For ErrorHandler above, replace handleError with this
#RequestMapping(ERROR_PATH)
ApiError handleError(WebRequest webRequest) {
var attrs = errorAttributes.getErrorAttributes(webRequest, ErrorAttributeOptions.of(ErrorAttributeOptions.Include.MESSAGE));
return new ApiError(
(Integer) attrs.get("status"),
(String) attrs.get("message"), // consider using predefined message(s) here
(String) attrs.get("path"));
}
Advice with validation exception handling
package no.mycompany.myapp.error;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.stream.Collectors;
#RestControllerAdvice
public class ExceptionHandlerAdvice {
private static final String ERROR_MSG = "validation error";
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
ApiError handleValidationException(MethodArgumentNotValidException exception, HttpServletRequest request) {
return new ApiError(
HttpStatus.BAD_REQUEST.value(),
ERROR_MSG,
request.getServletPath(),
exception.getBindingResult().getFieldErrors().stream()
.collect(Collectors.toMap(
FieldError::getField,
FieldError::getDefaultMessage,
// mergeFunction handling multiple errors for a field
(firstMessage, secondMessage) -> firstMessage)));
}
}
Related config in application.yml
server:
error:
include-message: always
include-binding-errors: always
When using application.properties
server.error.include-message=always
server.error.include-binding-errors=always
When using Spring Data JPA, consider using the following setting for turning off a second validation.
spring:
jpa:
properties:
javax:
persistence:
validation:
mode: none
More information on exception handling in Spring:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc (revised April 2018)
https://www.baeldung.com/exception-handling-for-rest-with-spring (December 31, 2020)

Spring WebFlux: How to access Request Body in HandlerFilterFunction

I'm trying to access the request body from WebFlux's HandlerFunctionFunction but I am getting java.lang.IllegalStateException: Only one connection receive subscriber allowed..
I want to do something similar to below code block
public class ExampleHandlerFilterFunction
implements HandlerFilterFunction<ServerResponse, ServerResponse> {
#Override
public Mono<ServerResponse> filter(ServerRequest serverRequest,
HandlerFunction<ServerResponse> handlerFunction) {
if (serverRequest.pathVariable("name").equalsIgnoreCase("test")) {
return serverRequest.bodyToMono(Player.class)
.doOnNext(loggerService :: logAndDoSomethingElse)
.then(handlerFunction.handle(serverRequest);
}
return handlerFunction.handle(serverRequest);
}
}
I tried serverRequest.bodyToMono(Player.class).cache() too, but did not work.
Update: Adding handler and router functions
Handler Function
#Component
public class PlayerHandler {
#Autowired
private final playerRepository;
public PlayerHandler(PlayerRepository palyerRepository) {
this.palyerRepository = playerRepository;
}
public Mono<ServerResponse> savePlayer(ServerRequest request) {
Mono<String> id = request.bodyToMono(Player.class)
.map(playerRepository::save)
.map(Player::getId);
return ok().body(id, String.class);
}
}
Router function
#Bean
public RouterFunction<ServerResponse> route(PlayerHandler playerHandler) {
return RouterFunctions
.route(POST("/players/"), playerHandler::save)
.filter(new ExampleHandlerFilterFunction());
}
Logger service
public Mono<Void> T logAndDoSomethingElse(T t){
---- auditing business logic----
return loggerRepository.save(asJsonb);
}
Can someone help me? Thanks
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerFilterFunction;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
#Component
public class FundsAuthorizationFilter implements HandlerFilterFunction<ServerResponse, ServerResponse> {
#Override
public Mono<ServerResponse> filter(ServerRequest request, HandlerFunction<ServerResponse> handlerFunction) {
String block = request.bodyToMono(String.class).block();
JSONObject jsonObj = new JSONObject(block);
ServerRequest.Builder newRequestBuilder = ServerRequest.from(request);
newRequestBuilder.body(block);
return handlerFunction.handle(newRequestBuilder.build());
}
}
I found a solution for this, that is clone the serverRequest and set the body to the new request

Specifying which validation group to use for a bean

Specs : hibernate-validator[5.2.4.Final], spring-context[4.2.2.RELEASE]
I am trying to make the solution described here work as below. But there are no constraint violations encountered & things just pass by fine. Why?
I have two beans, one parent , other child. The child definition is as below
package code;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
#Service("SampleBean")
#Validated
public class SampleBean {
#NotNull(message= "value can not be null" , groups = Group1.class)
// #NotNull(message= "value can not be null")
private Integer value;
#NotNull(message= "value1 can not be null" , groups = Group2.class)
// #NotNull(message= "value can not be null" )
private Integer value1;
public Integer getValue() {
return value;
}
public void setValue(#NotNull
Integer value) {
this.value = value;
}
public Integer getValue1() {
return value1;
}
public void setValue1(Integer value1) {
this.value1 = value1;
}
}
The Parent bean definition is as below :
package code;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
#Service("SampleBeanParent")
#Validated
public class SampleBeanParent {
public void acceptChildBean(#NotNull(message = "child cannot be null")
// #Valid
#Validated(Group1.class)
SampleBean childBean) throws NoSuchMethodException, SecurityException{
System.out.println("successfully finished");
}
}
The test class is as
package code;
import java.util.ArrayList;
import java.util.List;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) throws NoSuchMethodException, SecurityException{
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
SampleBean sampleBean = (SampleBean) context.getBean("SampleBean");
try{
SampleBeanParent parent = (SampleBeanParent) context.getBean("SampleBeanParent");
parent.acceptChildBean(sampleBean);
}
catch(ConstraintViolationException e){
System.out.println("there were validation errors");
}
}
}
By The way, i have setup appropriate spring level beans as below & it works fine without groups(i have commented the validation lines in above code without groups for the working case). So this is not the problem :)
package code;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
#Configuration
#ComponentScan(basePackageClasses = {SampleBean.class})
public class SpringConfiguration {
#Bean(name = "validator")
public LocalValidatorFactoryBean initValidatorFactory(){
return new LocalValidatorFactoryBean();
}
#Bean
public MethodValidationPostProcessor initValidationPostProcessor(){
return new MethodValidationPostProcessor();
}
}
I could do that the following way. The changed classes are as follows (I removed everything from the code that seemed to be redundant/unnecessary from the particular problem point of view)
#Service("SampleBeanParent")
#Validated(Group1.class)
public class SampleBeanParent {
public void acceptChildBean(
#Valid SampleBean childBean) throws NoSuchMethodException, SecurityException {
System.out.println("successfully finished");
}
}
#Service("SampleBean")
public class SampleBean {
#NotNull(message = "value can not be null", groups = Group1.class)
private Integer value;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
}
Please also refer to this thread: Spring #Validated in service layer, it contains several useful points, among others quoted from one of the answers:
"The #Validated annotation is only used to specify a validation group, it doesn't itself force any validation. You need to use one of the javax.validation annotations, like #Null or #Valid." - indicating that #Validated(Group1.class) SampleBean childBean in your example does not seem to be correct
The last answer is discussing the specific case when there is another annotation on the method parameter besides #Valid like in your case there was also #NotNull(message = "child cannot be null")

Error Handling that give JSON

I'm trying to create a simple error handling project, that will give JSON with error data after receiving an error (for example 404, 422 or 500). I work with code from this site, but it's not working for me.
I actually have this two classes:
BasicController class
package com.mycompany.jsonerrorhandler;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
/**
* Class to catch all exception
*/
public class BasicController
{
#ExceptionHandler (Exception.class)
#ResponseStatus (HttpStatus.INTERNAL_SERVER_ERROR)
public ModelAndView handleAllExceptions(Exception ex)
{
return new JsonError(ex.getMessage()).asModelAndView();
}
}
JsonError class
package com.mycompany.jsonerrorhandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJacksonJsonView;
import com.google.common.collect.ImmutableMap;
/**
* Class that defines what JSON Error looks like
*/
public class JsonError
{
private final String message;
public JsonError(String message)
{
this.message = message;
}
public ModelAndView asModelAndView()
{
MappingJacksonJsonView jsonView = new MappingJacksonJsonView();
return new ModelAndView(jsonView, ImmutableMap.of("error", message));
}
}
I wonder what I need to connect them and receive JSON (or maybe there is other solution for this problem).
Based on the like you provided, the JsonError class should contain the following:
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJacksonJsonView;
import com.google.common.collect.ImmutableMap;
public class JsonError
{
private final String message;
public JsonError(String message) {
this.message = message;
}
public ModelAndView asModelAndView() {
MappingJacksonJsonView jsonView = new MappingJacksonJsonView();
return new ModelAndView(jsonView, ImmutableMap.of("error", message));
}
}

Categories

Resources