在Spring-MVC控制器中触发404?


193

我如何获得春天 3.0控制器来触发404?

我有一个控制器,@RequestMapping(value = "/**", method = RequestMethod.GET)对于要访问该控制器的某些URL,我希望容器提供一个404。

Answers:


326

从Spring 3.0开始,您还可以抛出带有@ResponseStatus注释声明的Exception :

@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(); 
        }
    }
}

2
有趣。您可以指定在抛出站点使用哪个HttpStatus(即,没有将其编译为Exception类)吗?
马特b 2010年

1
@mattb:我想指出的@ResponseStatus是,您定义了一堆强类型的,命名合理的异常类,每个异常类都有自己的@ResponseStatus。这样,您就可以将控制器代码与HTTP状态代码的详细信息分离。
skaffman 2010年

9
可以扩展它以支持返回包含有关错误的更多描述的正文吗?
汤姆(Tom),

7
@Tom:@ResponseStatus(value = HttpStatus.NOT_FOUND, reason="Your reason")
Nailgun,2012年

6
如果仅将此ResourceNotFound异常用于流控制,则最好ResourceNotFound.fillInStackTrace()用空的实现覆盖。
2013年


36

重写您的方法签名,使其接受HttpServletResponse为参数,以便您可以对其进行调用setStatus(int)

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-arguments


8
这是唯一正确的答案,如果有人正在寻找一种方法来告诉http请求者他们犯了一个错误,同时又没有将无法解决的大量异常淹没于prod ops团队中。
Alex R

4
setStatus(int)javadoc声明如下:如果使用此方法设置错误代码,则不会触发容器的错误页面机制。如果存在错误,并且调用者希望调用Web应用程序中定义的错误页面,则sendError必须改用它。
Philippe Gioseffi '17

@AlexR处理的异常不应充斥于操作团队。如果是这样,则说明日志记录不正确。
Raedwald

25

我要提到的是,Spring默认提供(默认)404的一个例外。有关详细信息,请参见Spring文档。因此,如果您不需要自己的异常,则可以执行以下操作:

 @RequestMapping(value = "/**", method = RequestMethod.GET)
 public ModelAndView show() throws NoSuchRequestHandlingMethodException {
    if(something == null)
         throw new NoSuchRequestHandlingMethodException("show", YourClass.class);

    ...

  }

11
这似乎是针对特定情况-当Spring找不到处理程序时。有问题的情况是,Spring 可以找到处理程序,但是用户出于其他原因想要返回404。
罗伊·特鲁伊洛夫

2
当我的处理程序方法的ulr映射是动态的时,我正在使用它。根据@PathVariable我的观点,当实体不存在时,就没有请求处理。您是否认为使用带有注释的Exception更好/更清洁@ResponseStatus(value = HttpStatus.NOT_FOUND)
michal.kreuzman 2012年

1
在您的情况下听起来不错,但我不知道我是否建议您在提供的链接中找到例外,以处理所有必要的例外情况-有时您应该自己制定。
罗伊·特鲁伊洛夫

好吧,Spring提供了一个例外,仅为404提供了一个例外。他们应该将其命名为404Exception或创建了一个。但因为它是现在,我认为这是确定每当你需要一个404抛出这个
autra

好吧,从技术上讲没关系-您将发送404状态标头。但是自动错误消息-响应内容-是“没有名称的请求处理方法...”,这可能不是您想要显示给用户的内容。
Olli 2015年

24

从Spring 3.0.2开始,您可以作为控制器方法的结果返回ResponseEntity <T>

@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>比@ResponseBody注释更灵活-参见另一个问题


2
灵活的课程设置却没有声明式编程的好处
rohanagarwal

3
如果您在PROD中使用Sentry或类似的产品,并且不想以没有实际错误的错误向其发送垃圾邮件,则与针对这种非异常情况使用异常的解决方案相比,此解决方案要好得多。
Tobias Hermann

不要忘记如何用您的实际对象填充身体。通用“对象”示例:对象returnItemBody = new Object(); 返回ResponseEntity.status(HttpStatus.OK).body(returnItemBody);
granadaCoder

16

您可以使用@ControllerAdvice处理您的Exceptions,@ControllerAdvice注释类的默认行为将协助所有已知的Controller。

因此,当您有任何Controller抛出404错误时,它将被调用。

像下面这样:

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.NOT_FOUND)  // 404
    @ExceptionHandler(Exception.class)
    public void handleNoTFound() {
        // Nothing to do
    }
}

并在web.xml中映射此404响应错误,如下所示:

<error-page>
        <error-code>404</error-code>
        <location>/Error404.html</location>
</error-page>

希望对您有所帮助。


2
您已使用404状态代码映射了Exception(和子类)类型的异常。您是否曾经想到过内部服务器错误?您打算如何处理GlobalControllerExceptionHandler中的那些?
rohanagarwal

这不适用于REST控制器,返回空响应。
rustyx

10

如果您的控制器方法用于文件处理之类的东西,那么ResponseEntity将非常方便:

@Controller
public class SomeController {
    @RequestMapping.....
    public ResponseEntity handleCall() {
        if (isFound()) {
            return new ResponseEntity(...);
        }
        else {
            return new ResponseEntity(404);
        }
    }
}

9

尽管标记的答案是正确的,但有一种方法可以毫无例外地实现这一目标。服务正在返回Optional<T>搜索到的对象,HttpStatus.OK如果找到则映射到该对象,如果为空则映射到404。

@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); 
    }

}

我总体上喜欢这种方法,但是有时使用Optionals最终会成为反模式。并且在返回集合时变得复杂。
jfzr

7

我建议像这样抛出HttpClientErrorException

@RequestMapping(value = "/sample/")
public void sample() {
    if (somethingIsWrong()) {
        throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
    }
}

您必须记住,只有在将任何内容写入servlet输出流之前,才能完成此操作。


4
Spring HTTP客户端抛出该异常。Spring MVC似乎无法识别该异常。您正在使用哪个Spring版本?您是否有例外的404?
爱德华多

1
这将导致Spring Boot返回:Whitelabel Error Page \n .... \n There was an unexpected error (type=Internal Server Error, status=500). \n 404 This is your not found error
苗条的

这是HTTP客户端的例外,而不是控制器的例外。因此,在指定的上下文中使用它是不合适的。
阿列克谢

3

这有点晚了,但是如果您正在使用Spring Data REST,那么已经有org.springframework.data.rest.webmvc.ResourceNotFoundException 它也使用@ResponseStatus注释了。不再需要创建自定义运行时异常。


2

另外,如果您想从控制器返回404状态,则只需执行此操作

@RequestMapping(value = "/somthing", method = RequestMethod.POST)
@ResponseBody
public HttpStatus doSomthing(@RequestBody String employeeId) {
    try{
  return HttpStatus.OK;
    } 
    catch(Exception ex){ 
  return HttpStatus.NOT_FOUND;
    }
}

这样,如果您想从控制器返回404,您将收到404错误。


0

只需使用web.xml即可添加错误代码和404错误页面。但是请确保404错误页面一定不能位于WEB-INF下。

<error-page>
    <error-code>404</error-code>
    <location>/404.html</location>
</error-page>

这是最简单的方法,但是有一些限制。假设您要为此页面添加与添加其他页面相同的样式。这样就不能做到这一点。您必须使用@ResponseStatus(value = HttpStatus.NOT_FOUND)


这样做的方法,但HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;要从控制器代码中考虑。现在,从外部看,响应看起来与没有击中任何控制器的普通404没有什么不同。
Darryl Miles

这不会触发404,它只会处理发生的情况
Alex R

0

使用设置配置web.xml

<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>

创建新的控制器

   /**
     * 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;
        }
}

0

因为至少有十种做同一件事的方法总是好事:

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Something {
    @RequestMapping("/path")
    public ModelAndView somethingPath() {
        return new ModelAndView("/", HttpStatus.NOT_FOUND);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.