How do I get a Spring 3.0 controller to trigger a 404?
I have a controller with @RequestMapping(value = "/**", method = RequestMethod.GET)
and for some URLs accessing the controller, I want the container to come up with a 404.
How do I get a Spring 3.0 controller to trigger a 404?
I have a controller with @RequestMapping(value = "/**", method = RequestMethod.GET)
and for some URLs accessing the controller, I want the container to come up with a 404.
Since Spring 3.0 you also can throw an Exception declared with @ResponseStatus
annotation:
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
...
}
@Controller
public class SomeController {
@RequestMapping.....
public void handleCall() {
if (isFound()) {
// whatever
}
else {
throw new ResourceNotFoundException();
}
}
}
Rewrite your method signature so that it accepts HttpServletResponse
as a parameter, so that you can call setStatus(int)
on it.
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-arguments
I would like to mention that there's exception (not only) for 404 by default provided by Spring. See Spring documentation for details. So if you do not need your own exception you can simply do this:
@RequestMapping(value = "/**", method = RequestMethod.GET)
public ModelAndView show() throws NoSuchRequestHandlingMethodException {
if(something == null)
throw new NoSuchRequestHandlingMethodException("show", YourClass.class);
...
}
Since Spring 3.0.2 you can return ResponseEntity<T> as a result of the controller's method:
@RequestMapping.....
public ResponseEntity<Object> handleCall() {
if (isFound()) {
// do what you want
return new ResponseEntity<>(HttpStatus.OK);
}
else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
(ResponseEntity<T> is a more flexible than @ResponseBody annotation - see another question)
you can use the @ControllerAdvice to handle your Exceptions , The default behavior the @ControllerAdvice annotated class will assist all known Controllers.
so it will be called when any Controller you have throws 404 error .
like the following :
@ControllerAdvice
class GlobalControllerExceptionHandler {
@ResponseStatus(HttpStatus.NOT_FOUND) // 404
@ExceptionHandler(Exception.class)
public void handleNoTFound() {
// Nothing to do
}
}
and map this 404 response error in your web.xml , like the following :
<error-page>
<error-code>404</error-code>
<location>/Error404.html</location>
</error-page>
Hope that Helps .
If your controller method is for something like file handling then ResponseEntity
is very handy:
@Controller
public class SomeController {
@RequestMapping.....
public ResponseEntity handleCall() {
if (isFound()) {
return new ResponseEntity(...);
}
else {
return new ResponseEntity(404);
}
}
}
While the marked answer is correct there is a way of achieving this without exceptions. The service is returning Optional<T>
of the searched object and this is mapped to HttpStatus.OK
if found and to 404 if empty.
@Controller
public class SomeController {
@RequestMapping.....
public ResponseEntity<Object> handleCall() {
return service.find(param).map(result -> new ResponseEntity<>(result, HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
}
@Service
public class Service{
public Optional<Object> find(String param){
if(!found()){
return Optional.empty();
}
...
return Optional.of(data);
}
}
I'd recommend throwing HttpClientErrorException, like this
@RequestMapping(value = "/sample/")
public void sample() {
if (somethingIsWrong()) {
throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
}
}
You must remember that this can be done only before anything is written to servlet output stream.
Also if you want to return 404 status from your controller all you need is to do this
@RequestMapping(value = "/somthing", method = RequestMethod.POST)
@ResponseBody
public HttpStatus doSomthing(@RequestBody String employeeId) {
try{
return HttpStatus.OK;
}
catch(Exception ex){
return HttpStatus.NOT_FOUND;
}
}
By doing this you will receive a 404 error in case when you want to return a 404 from your controller.
This is a bit late, but if you are using Spring Data REST then there is already org.springframework.data.rest.webmvc.ResourceNotFoundException
It also uses @ResponseStatus
annotation. There is no need to create a custom runtime exception anymore.
Simply you can use web.xml to add error code and 404 error page. But make sure 404 error page must not locate under WEB-INF.
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
This is the simplest way to do it but this have some limitation. Suppose if you want to add the same style for this page that you added other pages. In this way you can't to that. You have to use the @ResponseStatus(value = HttpStatus.NOT_FOUND)
Configure web.xml with setting
<error-page>
<error-code>500</error-code>
<location>/error/500</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error/404</location>
</error-page>
Create new controller
/**
* Error Controller. handles the calls for 404, 500 and 401 HTTP Status codes.
*/
@Controller
@RequestMapping(value = ErrorController.ERROR_URL, produces = MediaType.APPLICATION_XHTML_XML_VALUE)
public class ErrorController {
/**
* The constant ERROR_URL.
*/
public static final String ERROR_URL = "/error";
/**
* The constant TILE_ERROR.
*/
public static final String TILE_ERROR = "error.page";
/**
* Page Not Found.
*
* @return Home Page
*/
@RequestMapping(value = "/404", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
public ModelAndView notFound() {
ModelAndView model = new ModelAndView(TILE_ERROR);
model.addObject("message", "The page you requested could not be found. This location may not be current.");
return model;
}
/**
* Error page.
*
* @return the model and view
*/
@RequestMapping(value = "/500", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
public ModelAndView errorPage() {
ModelAndView model = new ModelAndView(TILE_ERROR);
model.addObject("message", "The page you requested could not be found. This location may not be current, due to the recent site redesign.");
return model;
}
}