在 Spring-MVC 控制器中触发 404?
Posted
技术标签:
【中文标题】在 Spring-MVC 控制器中触发 404?【英文标题】:Trigger 404 in Spring-MVC controller? 【发布时间】:2011-01-05 05:37:12 【问题描述】:如何让Spring 3.0 控制器触发 404?
我有一个带有 @RequestMapping(value = "/**", method = RequestMethod.GET)
的控制器,并且对于一些 URLs 访问控制器,我希望容器提供 404。
【问题讨论】:
【参考方案1】:从 Spring 3.0 开始,您还可以抛出使用 @ResponseStatus
注释声明的异常:
@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();
【讨论】:
有趣。你能指定在抛出站点使用哪个 HttpStatus(即没有将其编译到 Exception 类中)吗? @mattb:我认为@ResponseStatus
的意义在于您定义了一大堆强类型、良好命名的异常类,每个都有自己的@ResponseStatus
。这样,您就可以将控制器代码与 HTTP 状态代码的详细信息分离。
可以扩展它以支持返回包含更多错误描述的正文吗?
@Tom: @ResponseStatus(value = HttpStatus.NOT_FOUND, reason="Your reason")
如果您将此 ResourceNotFound 异常仅用于流量控制,那么使用空实现覆盖 ResourceNotFound.fillInStackTrace()
可能是个好主意。【参考方案2】:
从 Spring 5.0 开始,您不一定需要创建额外的异常:
throw new ResponseStatusException(NOT_FOUND, "Unable to find resource");
此外,您可以使用一个内置异常覆盖多个场景,并且您拥有更多控制权。
查看更多:
ResponseStatusException (javadoc) https://www.baeldung.com/spring-response-status-exception【讨论】:
请注意,从 Spring Boot 2.3 开始,原因将不会发送,因为存在泄露信息的风险。【参考方案3】:重写你的方法签名,让它接受HttpServletResponse
作为参数,这样你就可以在它上面调用setStatus(int)
。
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-arguments
【讨论】:
如果有人正在寻找一种方法来告诉 http 请求者他们犯了一个错误,同时又不会用一堆他们无法修复的异常来淹没 prod ops 团队,那么这是唯一正确的答案。跨度>setStatus(int)
javadoc 声明如下: 如果使用该方法设置错误码,则不会触发容器的错误页面机制。如果出现错误并且调用者希望调用 Web 应用程序中定义的错误页面,则必须使用 sendError
。
@AlexR 处理的异常不应该淹没运营团队。如果是,则日志记录不正确。【参考方案4】:
我想提一下,Spring 默认提供的 404 有异常(不仅)。有关详细信息,请参阅Spring documentation。因此,如果您不需要自己的异常,您可以简单地这样做:
@RequestMapping(value = "/**", method = RequestMethod.GET)
public ModelAndView show() throws NoSuchRequestHandlingMethodException
if(something == null)
throw new NoSuchRequestHandlingMethodException("show", YourClass.class);
...
【讨论】:
这似乎是针对特定情况的 - 当 Spring 找不到处理程序时。有问题的情况是 Spring 可以 找到处理程序,但用户出于其他原因想要返回 404。 当我的处理程序方法的 ulr 映射是动态的时,我正在使用它。当基于@PathVariable
的实体不存在时,从我的角度来看,没有请求处理。您认为使用自己的带有@ResponseStatus(value = HttpStatus.NOT_FOUND)
注释的异常更好/更清洁吗?
在你的情况下听起来不错,但我不知道我会推荐你提供的链接中发现的异常来处理所有需要异常的情况 - 有时你应该自己做.
嗯,Spring 提供了一个异常,一个仅用于 404。他们应该将其命名为 404Exception 或创建一个。但就像现在一样,我认为只要你需要 404 就可以抛出这个。
好吧,从技术上讲没关系 - 您将发送 404 状态标头。但是自动错误消息 - 响应内容 - 是“没有名称的请求处理方法......”这可能不是您想要向用户显示的内容。【参考方案5】:
从 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
【讨论】:
当然很灵活,但会破坏声明式编程的好处 如果您在 PROD 中使用 Sentry 或类似工具,并且不想向它发送非实际错误的垃圾邮件,那么与使用异常处理此非异常的解决方案相比,此解决方案要好得多情况。 不要忘记如何填充主体(使用您的实际对象)。通用“对象”示例:对象 returnItemBody = new Object(); return ResponseEntity.status(HttpStatus.OK).body(returnItemBody);【参考方案6】:您可以使用@ControllerAdvice 来处理您的异常, @ControllerAdvice 注释类的默认行为将协助所有已知的控制器。
所以当你有任何控制器抛出 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>
希望有所帮助。
【讨论】:
您已将 Exception 类型的异常(和子类)映射为 404 状态代码。你有没有想过有内部服务器错误?您打算如何处理 GlobalControllerExceptionHandler 中的那些? 这不适用于 REST 控制器,返回一个空响应。【参考方案7】:虽然标记的答案是正确的,但有一种方法可以毫无例外地实现这一点。服务返回搜索对象的Optional<T>
,如果找到则映射到HttpStatus.OK
,如果为空则映射到404。
@Controller
public class SomeController
@RequestMapping.....
public ResponseEntity<Object> handleCall(@PathVariable String param)
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 有时会成为一种反模式。并且在返回集合时变得复杂。【参考方案8】:如果您的控制器方法是用于文件处理之类的,那么ResponseEntity
非常方便:
@Controller
public class SomeController
@RequestMapping.....
public ResponseEntity handleCall()
if (isFound())
return new ResponseEntity(...);
else
return new ResponseEntity(404);
【讨论】:
【参考方案9】:我建议抛出 HttpClientErrorException,像这样
@RequestMapping(value = "/sample/")
public void sample()
if (somethingIsWrong())
throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
您必须记住,这只能在将任何内容写入 servlet 输出流之前完成。
【讨论】:
Spring HTTP 客户端抛出该异常。 Spring MVC 似乎无法识别该异常。你用的是什么Spring版本?除了这个例外,您是否得到 404? 这会导致 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 客户端的例外,而不是控制器。所以在指定的上下文中使用它是不合适的。【参考方案10】:
这有点晚了,但是如果您使用的是Spring Data REST,那么已经有org.springframework.data.rest.webmvc.ResourceNotFoundException
它还使用@ResponseStatus
注释。不再需要创建自定义运行时异常。
【讨论】:
【参考方案11】:此外,如果您想从控制器返回 404 状态,您只需要这样做
@RequestMapping(value = "/something", method = RequestMethod.POST)
@ResponseBody
public HttpStatus doSomething(@RequestBody String employeeId)
try
return HttpStatus.OK;
catch (Exception ex)
return HttpStatus.NOT_FOUND;
这样做会收到 404 错误,以防您想从控制器返回 404。
【讨论】:
【参考方案12】:使用设置配置 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;
【讨论】:
【参考方案13】:因为至少有十种方法来做同样的事情总是好的:
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);
【讨论】:
【参考方案14】:只需使用 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 没有什么不同。
这不会触发 404,它只会在发生时处理它以上是关于在 Spring-MVC 控制器中触发 404?的主要内容,如果未能解决你的问题,请参考以下文章
HTTP 状态 404 - /spring-mvc/login