No handler found exception and static resources in spring boot - java

I have a Spring Boot web application where I catch my custom exceptions in ControllerAdvice class. The problem is that Spring Boot doesn't throw exception by default if no handler is found (it sends json back to a client).
What I want is to catch NoHandlerFoundException in my ControllerAdvice class. To make this possible I explicitly configured
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
This trick does the job and I can catch NoHandlerFoundException now but it disables Spring to auto-configure path to static resources. So all my static resources are not available for a client now. I tried to resolve this using one more configuration which doesn't help
spring.resources.static-locations=classpath:/resources/static/
Could anybody please advise how to map static resources in Spring Boot when auto-configuration was disabled with spring.resources.add-mappings=false?
Thanks!

If your static resources are limited to specific URL paths, you can configure only those paths to be handled by the Spring static resources handler. In this example, the /doc URL path is served by static resources in the /resources/static/doc/ folder in the classpath:
spring.mvc.static-path-pattern=/doc/**
spring.resources.static-locations=classpath:/resources/static/doc/
You'll need to remove this configuration:
spring.resources.add-mappings=false

I experienced the same issue and after some research, I found out that it is obviously not possible to have both options enabled (i.e. throwing NoHandlerFoundException by setting spring.mvc.throw-exception-if-no-handler-found=true AND serving static resources automatically).
Enabling the option to throw NoHandlerFoundException requires one to set spring.resources.add-mappings to false, otherwise it would not work. Furthermore, in my test setup it was not possible to disable spring.resources.add-mappings and specify the URLs for static resources manually (e.g. via application properties spring.mvc.static-path-pattern and spring.resources.static-locations or programmatically by overriding public void addResourceHandlers(ResourceHandlerRegistry registry)), because then the spring.resources.add-mappings=false setting seems to be overruled.
Finally, I implemented the following workaround for serving static resources manually via my own controller implementation:
#Controller
public class StaticWebContentController {
private Map<String, byte[]> cache = new HashMap<String,byte[]>();
#RequestMapping(value = "/css/{file}", method = RequestMethod.GET)
public ResponseEntity<byte[]> getCssFile(#PathVariable("file") String name){
ResponseEntity<byte[]> responseEntity = loadResource(".\\static\\css\\"+name,"text/css");
return responseEntity;
}
#RequestMapping(value = "/img/bootstrap-icons-1.1.0/{file}", method = RequestMethod.GET)
public ResponseEntity<byte[]> getimgFile(#PathVariable("file") String name){
ResponseEntity<byte[]> responseEntity = loadResource(".\\static\\img\\bootstrap-icons-1.1.0\\"+name,"image/svg+xml");
return responseEntity;
}
#RequestMapping(value = "/js/{file}", method = RequestMethod.GET)
public ResponseEntity<byte[]> getJsFile(#PathVariable("file") String name){
ResponseEntity<byte[]> responseEntity = loadResource(".\\static\\js\\"+name,"text/javascript");
return responseEntity;
}
private ResponseEntity<byte[]> loadResource(String path, String contentType){
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", contentType);
if(hasCachedContent(path)){
return new ResponseEntity<byte[]>(getCachedContent(path),responseHeaders,HttpStatus.OK);
}else{
Resource resource = new ClassPathResource(path);
if(resource.exists()){
try{
InputStream inputStream = resource.getInputStream();
byte[] content = inputStream.readAllBytes();
putCache(path, content);
return new ResponseEntity<byte[]>(content,responseHeaders,HttpStatus.OK);
}catch(IOException e){
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR,e.getMessage());
}
}else{
throw new ResponseStatusException(HttpStatus.NOT_FOUND,"The requested resource '"+path+"' does not exist'");
}
}
}
private byte[] getCachedContent(String path){
return cache.get(path);
}
private boolean hasCachedContent(String path){
return cache.containsKey(path);
}
private void putCache(String path, byte[] content){
cache.put(path, content);
}
}
In my application, I have three types of static resources located in three different sub folders. Each type is handled by a separate endpoint in order to set the Content-Type header properly. Moreover, the controller caches each resource in order to avoid to reload the requested resource from hard disk again.
Probably, this is not the best solution, however, a feasible workaround in case of my application. Any recommendations for improvement are highly appreciated!

Instead of adding below lines to config properties
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
write your custom Error Attributes as below:
#Configuration
public class CustomErrorAttributes extends DefaultErrorAttributes {
#Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
#Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
Map<String, Object> newErrorAttributes = new LinkedHashMap<String, Object>();
Object errorMessage = requestAttributes.getAttribute(RequestDispatcher.ERROR_MESSAGE, RequestAttributes.SCOPE_REQUEST);
if (errorMessage != null) {
newErrorAttributes.put("response-type", "error");
newErrorAttributes.put("error-code", errorAttributes.get("status"));
newErrorAttributes.put("message", errorAttributes.get("message"));
newErrorAttributes.put("error-message", errorAttributes.get("error"));
}
return newErrorAttributes;
}
};
}
}

Related

Spring boot & Java - HTTP Status 404 error aka white-label error

Please have a look at my codes below. The Java codes seemed to work just fine, but localhost:8080 gives me the error code 404 when I try to access it. I want to make localhost 8080 work. Please let me know if you need further information.
Application
#SpringBootApplication(exclude = { ErrorMvcAutoConfiguration.class })
// exclude part is to elimnate whitelabel error
#EnableScheduling
public class Covid19TrackerApplication {
public static void main(String[] args) {
SpringApplication.run(Covid19TrackerApplication.class, args);
}
}
Controller
#Controller
public class HomeController {
CovidDataService covidDataService;
#RequestMapping("/")
public #ResponseBody String home(Model model) {
model.addAttribute( "locationStats", covidDataService.getAllStats());
return "home";
}
}
Main Code
#Service
public class CovidDataService {
private static String Covid_Data_URL = "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv";
private List<LocationStats> allStats = new ArrayList<>();
public List<LocationStats> getAllStats() {
return allStats;
}
#PostConstruct//?
#Scheduled(cron = "* * 1 * * *") //????
// * sec * min *hour and so on
public void fetchCovidData() throws IOException, InterruptedException {
List<LocationStats> newStats = new ArrayList<>(); // why we are adding this? To prevent user get an error while we are working on new data.
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(Covid_Data_URL))
.build(); // uri = uniform resource identifier
HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
StringReader csvBodyReader = new StringReader(httpResponse.body()); //StringReader needs to be imported
Iterable<CSVRecord> records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(csvBodyReader); // parse(in) had error, we needed a "reader" instance.
for (CSVRecord record : records) {
LocationStats locationStat = new LocationStats(); //create an instance
locationStat.setState(record.get("Province/State"));
locationStat.setCountry(record.get("Country/Region"));
locationStat.setLatestTotalCase(Integer.parseInt(record.get(record.size()-1)));
System.out.println(locationStat);
newStats.add(locationStat);
}
this.allStats = newStats;
}
}
The problem may come from this piece of code
#RequestMapping("/")
public #ResponseBody String home(Model model) {
model.addAttribute( "locationStats", covidDataService.getAllStats());
return "home";
}
it returns "home" which should be existing view, normally, the view will be a jsp file which is placed somewhere in WEB-INF, please see this tutorial: https://www.baeldung.com/spring-mvc-view-resolver-tutorial
In the case of wrong mapping, it may returns 404 error
when you run the server, you should be able to see which port it's taken in the console.
Also, is server.port=8080 in the src/main/resources/application.properties file?
In the controller, the RequestMapping annotation is missing the method type and header
#RequestMapping(
path="/",
method= RequestMethod.GET,
produces=MediaType.APPLICATION_JSON_VALUE)
public String home(Model model) {
model.addAttribute( "locationStats", covidDataService.getAllStats());
return "home";
}
make sure to add consumes for POST or PUT methods
A bit unrelated to the question but the line in the controller is missing #Autowired annotation
CovidDataService covidDataService;
Preferrably, add the #Autowired in the constructor
#Autowired
public HomeController(CovidDataService covidDataService) {
this.covidDataService = covidDataService;
}

How to ignore any http content headers on Spring #RestController?

I have some webservice endpoints that should offer json data by default. Therefore configuring as follows:
#Configuration
public class ContentNegotiationConfiguration implements WebMvcConfigurer {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
}
Problem: now I want to create an endpoint that offers a file download (thus is not json).
#RestController
public class FileServlet {
#GetMapping(value = "/docs/{filename}", consumes = MediaType.ALL_VALUE, produces = APPLICATION_OCTET_STREAM_VALUE)
public Object download(#Pathvariable filename) {
File file = fileservice.resolve(filename);
return new FileSystemResource(file);
}
}
Accessing this endpoint from the browser works fine. I can download the files.
But: when using native clients that are not setting any http headers like content-type, accept-header etc, the access fails with:
WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver: Resolved
[org.springframework.web.HttpMediaTypeNotAcceptableException:
Could not find acceptable representation]
All of them result in the exception:
curl localhost:8080/docs/testfile.txt
curl -O localhost:8080/docs/testfile.txt
wget localhost:8080/docs/testfile.txt
This is probably because I set the default content type to json above in ContentNegotiationConfiguration. I cannot change that due to all the other endpoints that should be json by default.
Question: how can I explicit ignore that default json setting on that single endpoint, and always just offer the download stream?
Try custom ContentNegotiationStrategy with AntPathMatcher something like:
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// configurer.defaultContentType(MediaType.APPLICATION_JSON,MediaType.APPLICATION_OCTET_STREAM);
configurer.defaultContentTypeStrategy(
new ContentNegotiationStrategy() {
private UrlPathHelper urlPathHelper = new UrlPathHelper();
AntPathMatcher antPathMatcher = new AntPathMatcher();
#Override
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request == null) {
return null;
}
String path = this.urlPathHelper.getLookupPathForRequest(request);
if (antPathMatcher.match("/docs/*", path)) {
return Collections.singletonList(MediaType.APPLICATION_OCTET_STREAM);
} else {
return Collections.singletonList(MediaType.APPLICATION_JSON);
}
}
});
}
With the hint from #M. Deinum, I got it working as follows:
#GetMapping(value = "/docs/{filename}")
public void download(#Pathvariable filename) {
FileSystemResource file = new FileSystemResource(fileservice.resolve(filename));
rsp.setHeader("Content-Disposition", "attachment; filename=" + file.getFilename());
ResourceHttpMessageConverter handler = new ResourceHttpMessageConverter();
handler.write(file, MediaType.APPLICATION_OCTET_STREAM, new ServletServerHttpResponse(rsp));
}
That way writing directly to the stream bypassing the content negotiation, while still relying on the Spring class ResourceHttpMessageConverter for not having to implement the response writer myself.

How to return an image in Spring Boot controller and serve like a file system

I've tried the various ways given in Stackoverflow, maybe I missed something.
I have an Android client (whose code I can't change) which is currently getting an image like this:
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
Where url is the location of the image (static resource on CDN). Now my Spring Boot API endpoint needs to behave like a file resource in the same way so that the same code can get images from the API (Spring boot version 1.3.3).
So I have this:
#ResponseBody
#RequestMapping(value = "/Image/{id:.+}", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<byte[]> getImage(#PathVariable("id")String id) {
byte[] image = imageService.getImage(id); //this just gets the data from a database
return ResponseEntity.ok(image);
}
Now when the Android code tries to get http://someurl/image1.jpg I get this error in my logs:
Resolving exception from handler [public
org.springframework.http.ResponseEntity
com.myproject.MyController.getImage(java.lang.String)]:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not
find acceptable representation
Same error happens when I plug http://someurl/image1.jpg into a browser.
Oddly enough my tests check out ok:
Response response = given()
.pathParam("id", "image1.jpg")
.when()
.get("MyController/Image/{id}");
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
byte[] array = response.asByteArray(); //byte array is identical to test image
How do I get this to behave like an image being served up in the normal way? (Note I can't change the content-type header that the android code is sending)
EDIT
Code after comments (set content type, take out produces):
#RequestMapping(value = "/Image/{id:.+}", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE)
public ResponseEntity<byte[]> getImage(#PathVariable("id")String id, HttpServletResponse response) {
byte[] image = imageService.getImage(id); //this just gets the data from a database
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
return ResponseEntity.ok(image);
}
In a browser this just seems to give a stringified junk (byte to chars i guess). In Android it doesn't error, but the image doesn't show.
I believe this should work:
#RequestMapping(value = "/Image/{id:.+}", method = RequestMethod.GET)
public ResponseEntity<byte[]> getImage(#PathVariable("id") String id) {
byte[] image = imageService.getImage(id);
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).body(image);
}
Notice that the content-type is set for ResponseEntity, not for HttpServletResponse directly.
Finally fixed this... I had to add a ByteArrayHttpMessageConverter to my WebMvcConfigurerAdapter subclass:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
final List<MediaType> list = new ArrayList<>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.APPLICATION_OCTET_STREAM);
arrayHttpMessageConverter.setSupportedMediaTypes(list);
converters.add(arrayHttpMessageConverter);
super.configureMessageConverters(converters);
}
In case you don't know the file/mime type you can do this.... I've done this where i take an uploaded file and replace the file name with a guid and no extension and browsers / smart phones are able to load the image no issues.
the second is to serve a file to be downloaded.
#RestController
#RequestMapping("img")
public class ImageController {
#GetMapping("showme")
public ResponseEntity<byte[]> getImage() throws IOException{
File img = new File("src/main/resources/static/test.jpg");
return ResponseEntity.ok().contentType(MediaType.valueOf(FileTypeMap.getDefaultFileTypeMap().getContentType(img))).body(Files.readAllBytes(img.toPath()));
}
#GetMapping("thing")
public ResponseEntity<byte[]> what() throws IOException{
File file = new File("src/main/resources/static/thing.pdf");
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=" +file.getName())
.contentType(MediaType.valueOf(FileTypeMap.getDefaultFileTypeMap().getContentType(file)))
.body(Files.readAllBytes(file.toPath()));
}
}
UPDATE in java 9+ you need to add compile 'com.sun.activation:javax.activation:1.2.0' to your dependencies this has also been moved or picked up by jakarta.see this post
Using Apache Commons, you can do this and expose the image on an endpoint
#RequestMapping(value = "/image/{imageid}",method= RequestMethod.GET,produces = MediaType.IMAGE_JPEG_VALUE)
public #ResponseBody byte[] getImageWithMediaType(#PathVariable int imageid) throws IOException {
InputStream in = new ByteArrayInputStream(getImage(imageid));
return IOUtils.toByteArray(in);
}
All images will be served at endpoint /image/{imageid}

Angular get image from Spring RestController and cache it

I have a Client-Server application using SpringBoot and Angular2.
I would like to load a image from the server by filename. This works fine.
I store the attribute image:string at the client and I place it in the template again.
You might pay attention to return res.url;; I do not use the actual ressource, which might be wrong.
My objective is that image is cached. To my understanding the web-browser can automatically cache the images. Correct?
But the caching does not work yet and maybe somebody could give me a hint what needs to be adjusted?
Is a different header required?
Server (SpringBoot)
public class ImageRestController {
#RequestMapping(value = "/getImage/{filename:.+}", method = RequestMethod.GET)
public ResponseEntity<Resource> getImage(#PathVariable String filename) {
try {
String path = Paths.get(ROOT, filename).toString();
Resource loader = resourceLoader.getResource("file:" + path);
return new ResponseEntity<Resource>(loader, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<Resource>(HttpStatus.NOT_FOUND);
}
}
}
Client (Angular2)
#Component({
selector: 'my-image',
template: `
<img src="{{image}}"/>
`
})
export class MyComponent {
image:string;
constructor(private service:MyService) {}
showImage(filename:string) {
this.service.getImage(filename)
.subscribe((file) => {
this.image = file;
});
}
}
export class MyService() {
getImage(filename:String):Observable<any> {
return this.http.get(imagesUrl + "getImage/" + filename)
.map(this.extractUrl)
.catch(this.handleError);
}
extractUrl(res:Response):string {
return res.url;
}
}
You could do something like this on the server side (and perhaps add an ETag or Last-Modified header if you can get that information):
return ResponseEntity
.ok()
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
.body(loader);
See the HTTP caching part of the reference documentation in Spring.
If you're just serving resources and not applying any additional logic, then you'd better do the following:
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/getImage/**")
.addResourceLocations("classpath:/path/to/root/")
.setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS).cachePublic());
}
}
See the other relevant part of the reference documentation. You can also apply transformations and leverage cache busting (see this section as well).

How to handle exceptions in Spring MVC differently for HTML and JSON requests

I'm using the following exception handler in Spring 4.0.3 to intercept exceptions and display a custom error page to the user:
#ControllerAdvice
public class ExceptionHandlerController
{
#ExceptionHandler(value = Exception.class)
public ModelAndView handleError(HttpServletRequest request, Exception e)
{
ModelAndView mav = new ModelAndView("/errors/500"));
mav.addObject("exception", e);
return mav;
}
}
But now I want a different handling for JSON requests so I get JSON error responses for this kind of requests when an exception occurred. Currently the above code is also triggered by JSON requests (Using an Accept: application/json header) and the JavaScript client doesn't like the HTML response.
How can I handle exceptions differently for HTML and JSON requests?
The ControllerAdvice annotation has an element/attribute called basePackage which can be set to determine which packages it should scan for Controllers and apply the advices. So, what you can do is to separate those Controllers handling normal requests and those handling AJAX requests into different packages then write 2 Exception Handling Controllers with appropriate ControllerAdvice annotations. For example:
#ControllerAdvice("com.acme.webapp.ajaxcontrollers")
public class AjaxExceptionHandlingController {
...
#ControllerAdvice("com.acme.webapp.controllers")
public class ExceptionHandlingController {
The best way to do this (especially in servlet 3) is to register an error page with the container, and use that to call a Spring #Controller. That way you get to handle different response types in a standard Spring MVC way (e.g. using #RequestMapping with produces=... for your machine clients).
I see from your other question that you are using Spring Boot. If you upgrade to a snapshot (1.1 or better in other words) you get this behaviour out of the box (see BasicErrorController). If you want to override it you just need to map the /error path to your own #Controller.
As you have the HttpServletRequest, you should be able to get the request "Accept" header. Then you could process the exception based on it.
Something like:
String header = request.getHeader("Accept");
if(header != null && header.equals("application/json")) {
// Process JSON exception
} else {
ModelAndView mav = new ModelAndView("/errors/500"));
mav.addObject("exception", e);
return mav;
}
Since i didn't find any solution for this, i wrote some code that manually checks the accept header of the request to determine the format. I then check if the user is logged in and either send the complete stacktrace if he is or a short error message.
I use ResponseEntity to be able to return both JSON or HTML like here.
Code:
#ExceptionHandler(Exception.class)
public ResponseEntity<?> handleExceptions(Exception ex, HttpServletRequest request) throws Exception {
final HttpHeaders headers = new HttpHeaders();
Object answer; // String if HTML, any object if JSON
if(jsonHasPriority(request.getHeader("accept"))) {
logger.info("Returning exception to client as json object");
headers.setContentType(MediaType.APPLICATION_JSON);
answer = errorJson(ex, isUserLoggedIn());
} else {
logger.info("Returning exception to client as html page");
headers.setContentType(MediaType.TEXT_HTML);
answer = errorHtml(ex, isUserLoggedIn());
}
final HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return new ResponseEntity<>(answer, headers, status);
}
private String errorHtml(Exception e, boolean isUserLoggedIn) {
String error = // html code with exception information here
return error;
}
private Object errorJson(Exception e, boolean isUserLoggedIn) {
// return error wrapper object which will be converted to json
return null;
}
/**
* #param acceptString - HTTP accept header field, format according to HTTP spec:
* "mime1;quality1,mime2;quality2,mime3,mime4,..." (quality is optional)
* #return true only if json is the MIME type with highest quality of all specified MIME types.
*/
private boolean jsonHasPriority(String acceptString) {
if (acceptString != null) {
final String[] mimes = acceptString.split(",");
Arrays.sort(mimes, new MimeQualityComparator());
final String firstMime = mimes[0].split(";")[0];
return firstMime.equals("application/json");
}
return false;
}
private static class MimeQualityComparator implements Comparator<String> {
#Override
public int compare(String mime1, String mime2) {
final double m1Quality = getQualityofMime(mime1);
final double m2Quality = getQualityofMime(mime2);
return Double.compare(m1Quality, m2Quality) * -1;
}
}
/**
* #param mimeAndQuality - "mime;quality" pair from the accept header of a HTTP request,
* according to HTTP spec (missing mimeQuality means quality = 1).
* #return quality of this pair according to HTTP spec.
*/
private static Double getQualityofMime(String mimeAndQuality) {
//split off quality factor
final String[] mime = mimeAndQuality.split(";");
if (mime.length <= 1) {
return 1.0;
} else {
final String quality = mime[1].split("=")[1];
return Double.parseDouble(quality);
}
}
The trick is to have a REST controller with two mappings, one of which specifies "text/html" and returns a valid HTML source. The example below, which was tested in Spring Boot 2.0, assumes the existence of a separate template named "error.html".
#RestController
public class CustomErrorController implements ErrorController {
#Autowired
private ErrorAttributes errorAttributes;
private Map<String,Object> getErrorAttributes( HttpServletRequest request ) {
WebRequest webRequest = new ServletWebRequest(request);
boolean includeStacktrace = false;
return errorAttributes.getErrorAttributes(webRequest,includeStacktrace);
}
#GetMapping(value="/error", produces="text/html")
ModelAndView errorHtml(HttpServletRequest request) {
return new ModelAndView("error.html",getErrorAttributes(request));
}
#GetMapping(value="/error")
Map<String,Object> error(HttpServletRequest request) {
return getErrorAttributes(request);
}
#Override public String getErrorPath() { return "/error"; }
}
References
ModelAndView -- return type for HTML
DefaultErrorAttributes -- data used to render HTML template (and JSON response)
BasicErrorController.java -- Spring Boot source from which this example was derived
The controlleradvice annotation has several properties that can be set, since spring 4. You can define multiple controller advices applying different rules.
One property is "annotations. Probably you can use a specific annotation on the json request mapping or you might find another property more usefull?
Use #ControllerAdvice
Let the exception handler send a DTO containing the field errors.
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ValidationErrorDTO processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return processFieldErrors(fieldErrors);
}
This code is of this website:http://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/
Look there for more info.

Categories

Resources