I'm new to Spring Boot but after few hours of reading posts and blogs about exception handlig in Spring Boot REST where nobody wrote anything about handling such exception thrown from custom Converter, I decided to write here.
I develop small REST app based on Spring Boot simply generated from IntelliJ. Exemplary method looks like this
@RestController
@RequestMapping("/resources")
public class CVResourceService {
private final TechnologyRepository technologyRepository;
private final ProjectRepository projectRepository;
@Autowired
public CVResourceService(TechnologyRepository technologyRepository, ProjectRepository projectRepository) {
this.technologyRepository = technologyRepository;
this.projectRepository = projectRepository;
}
@RequestMapping(value = "/users/{guid}/projects/langs/{lang}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Collection getUserProjects(@PathVariable("guid") GUID userGUID, @PathVariable("lang") Language language) {
return ProjectDTOAssembler.toDTOs(projectRepository.findOne(userGUID, language));
}
}
Because both guid and lang are String and I wanted this pieces of information were strong typed from same begining, I created simply converters for GUID and Language types and registered it in Application class:
public final class GUIDConverter implements Converter{
@Override
public GUID convert(String source) {
return GUID.fromString(source);
}
}
public class LanguageConverter implements Converter{
@Override
public Language convert(String source) {
Language language = Language.of(source);
if (language == null) { throw new WrongLanguagePathVariableException(); }
return language;
}
}
GUID throws exception from method factory,
...
public static GUID fromString(String string) {
String[] components = string.split("-");
if (components.length != 5)
throw new IllegalArgumentException("Invalid GUID string: " + string);
return new GUID(string);
}
...
Language return null so then I throw custom exception from converter.
Registering in Application:
@SpringBootApplication
public class Application extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new GUIDConverter());
registry.addConverter(new LanguageConverter());
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Using all kind of handling exception with @ResponseStatus, @ControllerAdvice and @ExpectationHandler I couldn't catch converters' exceptions in Controller to rewrite (or better map) "status", "error", "exception" and "message" of json error response original field to my values. Probably because the exceptions are thrown before the call of my REST method. I tried also solution with ResponseEntityExceptionHandler but it didn't work at all.
For the request http://localhost:8080/resources/users/620e643f-406f-4c69-3f4c-3f2c303f3f3f/projects/langs/end where correct language is en, response with exception is:
{
"timestamp": 1458812172976,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException",
"message": "Failed to convert value of type [java.lang.String] to required type [com.cybercom.cvdataapi.domain.Language]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.PathVariable com.cybercom.cvdataapi.domain.Language] for value 'end'; nested exception is com.cybercom.cvdataapi.interfaces.rest.converter.WrongLanguagePathVariableException",
"path": "/resources/users/620e643f-406f-4c69-3f4c-3f2c303f3f3f/projects/langs/end"
}
where custom exception is only at the last position in message field but of course should be exchange with my custom message.And custom excetion should be in exception field where is now Spring exception. it's the goal of course, bu no idea how to achieve it in this context.
Please solve my problem with catching exceptions thrown from converters and mapping them the way as it can be done with @ControllerAdvice and exceptions thrown from Controllers.
Thx in advance.