Arun
Arun

Reputation: 2342

Spring Boot Exception Handling

We are using spring boot for our application. I'm trying to return the localized result in the response when the Rest service is called based on the Accept-Language header. For example if the Accept-Language header is : zh,ja;q=0.8,en. So the response will be in chinese since we support that.

But if Accept-Language header is : zh1,ja;q=0.8,en. Then I get Internal server error like below, because it can't invoke @ExceptionHandler i do not get response i like. Below is what I get

{
  "timestamp": 1462213062585,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "java.lang.IllegalArgumentException",
  "message": "java.lang.IllegalArgumentException: range=zh1",
  "path": "/user/v1//paymentmethods/creditdebitcards"
}

Instead this is what I want to throw because for all other exceptions we handle and throw a similar response.

{
  "operation": {
    "result": "ERROR",
    "errors": [
      {
        "code": "1000",
        "message": "An unidentified exception has occurred.",
        "field": ""
      }
    ],
    "requestTimeStampUtc": "2016-05-02T18:22:03.356Z",
    "responseTimeStampUtc": "2016-05-02T18:22:03.359Z"
  }
}

Below are my classes, if the header is wrong (like zh1,ja;q=0.8,en) then the parse method below throws 500 error like above.

public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {

    @Autowired
    ExceptionHandling exceptionHandling;

    @Autowired
    MessageHandler    messageHandler;

    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        try {
            List<LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
            if (!list.isEmpty()) {
                for (LanguageRange s : list) {
                    if (ApplicationConstants.LOCALE.contains(s.getRange())) {
                        return Locale.forLanguageTag(s.getRange());
                    }
                }
            }
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(e);
        }
        return request.getLocale();
    }

Below is the ExceptionHandler class

@EnableWebMvc
@ControllerAdvice
public class ExceptionHandling extends ResponseEntityExceptionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandling.class);

    @Autowired
    private MessageHandler      messageHandler;

    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(value = { UnsupportedMediaTypeException.class, InvalidMediaTypeException.class })
    public void unsupportedMediaTypeException() {

    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = Exception.class)
    public @ResponseBody OperationsErrorBean handleglobalException(final HttpServletRequest request,
            final Exception ex) {
        LOGGER.error("Unhandled Exception Occurred: ", ex);
        return errorResponse("1000", messageHandler.localizeErrorMessage("error.1000"), "", request.getRequestURI(),
                request.getAttribute("startTime").toString());

    }
}

This is my ApplicationConfig.java class

@Configuration
@ComponentScan("com.hsf")
@EnableWebMvc
public class ApplicationConfig extends WebMvcConfigurerAdapter {

    @Value("${spring.application.name}")
    String appName;

    @Bean
    public AlwaysSampler defaultSampler() {
        return new AlwaysSampler();
    }

    @Override
    public void addInterceptors(final InterceptorRegistry registry) {

        if (StringUtils.isNotBlank(appName)) {
            MDC.put("AppName", appName);

        } else {
            MDC.put("AppName", "APPNAME_MISSING");
        }

        registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/user/v1/**");
    }

    @Bean
    public LocaleResolver localeResolver() {
        return new SmartLocaleResolver();
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        final DispatcherServlet servlet = new DispatcherServlet();
        servlet.setDispatchOptionsRequest(true);
        return servlet;
    }

    @Bean
    public MessageSource messageSource() {
        final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:i18n/messages");
        // If true, the key of the message will be displayed if the key is not
        // found, instead of throwing an exception
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setDefaultEncoding("UTF-8");
        // The value 0 means always reload the messages to be developer friendly
        messageSource.setCacheSeconds(10);
        return messageSource;
    }

}

Upvotes: 1

Views: 6866

Answers (2)

Arun
Arun

Reputation: 2342

I have the Accept-Language check in the interceptor and I throw a custom exception I created when there is an exception parsing the header. So I throw a 400 Bad request with a proper response I want to display.

 public class RequestInterceptor extends HandlerInterceptorAdapter {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {

                final String startTime = DateUtils.getUTCDate();

                request.setAttribute("startTime", startTime);


                **try {
                    Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
                } catch (IllegalArgumentException e) {
                    throw new InvalidAcceptLanguageException();
                }**

                return true;

            }
        }

I have added a method in my ExceptionHandling class to throw InvalidAcceptLanguageException.

@EnableWebMvc
@ControllerAdvice
public class ExceptionHandling extends ResponseEntityExceptionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandling.class);


    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = InvalidAcceptLanguageException.class)
    @ResponseBody
    public OperationsErrorBean invalidAcceptLanguageException(final HttpServletRequest request, final Exception ex) {
        return errorResponse("N/A", "Accept-Language is not in correct format", "", request.getRequestURI(),
                request.getAttribute("startTime").toString());
    }
}

Upvotes: 0

Adam Siemion
Adam Siemion

Reputation: 16039

The @ExceptionHandler annotation for the unsupportedMediaTypeException method does not contain IllegalArgumentException, instead of:

@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler(value = { UnsupportedMediaTypeException.class, 
  InvalidMediaTypeException.class })
public void unsupportedMediaTypeException() { }

it should be:

@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler(value = { UnsupportedMediaTypeException.class, 
  InvalidMediaTypeException.class, IllegalArgumentException.class })
public void unsupportedMediaTypeException() { }

Also, since it seems handling of numerous languages is one of requirements of your application I suggest to create a dedicated RuntimeException for this situation InvalidAcceptLanguageException instead of using a generic IllegalArgumentException for this purpose.

Upvotes: 3

Related Questions