Servlet Web 应用程序
Spring Web MVC 框架(通常称为 “Spring MVC”)是一个功能丰富的“模型-视图-控制器” Web 框架。Spring MVC 允许你创建特殊的 @Controller 或 @RestController bean 来处理传入的 HTTP 请求。控制器中的方法通过使用 @RequestMapping 注解映射到 HTTP。
以下代码展示了一个典型的用于提供 JSON 数据的 @RestController:
Java
Kotlin
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users")
public class MyRestController {
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) {
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@GetMapping("/{userId}")
public User getUser(@PathVariable Long userId) {
return this.userRepository.findById(userId).get();
}
@GetMapping("/{userId}/customers")
public List
return this.userRepository.findById(userId).map(this.customerRepository::findByUser).get();
}
@DeleteMapping("/{userId}")
public void deleteUser(@PathVariable Long userId) {
this.userRepository.deleteById(userId);
}
}
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val customerRepository: CustomerRepository) {
@GetMapping("/{userId}")
fun getUser(@PathVariable userId: Long): User {
return userRepository.findById(userId).get()
}
@GetMapping("/{userId}/customers")
fun getUserCustomers(@PathVariable userId: Long): List
return userRepository.findById(userId).map(customerRepository::findByUser).get()
}
@DeleteMapping("/{userId}")
fun deleteUser(@PathVariable userId: Long) {
userRepository.deleteById(userId)
}
}
“WebMvc.fn” 是其函数式变体,它将路由配置与请求的实际处理分离,如下例所示:
Java
Kotlin
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.RequestPredicate;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.web.servlet.function.RequestPredicates.accept;
import static org.springframework.web.servlet.function.RouterFunctions.route;
@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {
private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);
@Bean
public RouterFunction
return route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build();
}
}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.servlet.function.RequestPredicates.accept
import org.springframework.web.servlet.function.RouterFunction
import org.springframework.web.servlet.function.RouterFunctions
import org.springframework.web.servlet.function.ServerResponse
@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {
@Bean
fun routerFunction(userHandler: MyUserHandler): RouterFunction
return RouterFunctions.route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build()
}
companion object {
private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
}
}
Java
Kotlin
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
@Component
public class MyUserHandler {
public ServerResponse getUser(ServerRequest request) {
...
}
public ServerResponse getUserCustomers(ServerRequest request) {
...
}
public ServerResponse deleteUser(ServerRequest request) {
...
}
}
import org.springframework.stereotype.Component
import org.springframework.web.servlet.function.ServerRequest
import org.springframework.web.servlet.function.ServerResponse
@Component
class MyUserHandler {
fun getUser(request: ServerRequest?): ServerResponse {
...
}
fun getUserCustomers(request: ServerRequest?): ServerResponse {
...
}
fun deleteUser(request: ServerRequest?): ServerResponse {
...
}
}
Spring MVC 是核心 Spring Framework 的一部分,详细信息可在参考文档中找到。spring.io/guides 上也有几篇涵盖 Spring MVC 的指南。
你可以定义任意数量的 RouterFunction bean 来模块化路由的定义。如果需要应用优先级,可以对 bean 进行排序。
Spring MVC 自动配置
Spring Boot 为 Spring MVC 提供了自动配置,这适用于大多数应用程序。它取代了对 @EnableWebMvc 的需求,两者不能一起使用。除了 Spring MVC 的默认设置外,自动配置还提供了以下特性:
包含 ContentNegotiatingViewResolver 和 BeanNameViewResolver bean。
支持提供静态资源,包括对 WebJars 的支持(本文档后续会介绍)。
自动注册 Converter、GenericConverter 和 Formatter bean。
支持 HttpMessageConverters(本文档后续会介绍)。
自动注册 MessageCodesResolver(本文档后续会介绍)。
静态 index.html 支持。
自动使用 ConfigurableWebBindingInitializer bean(本文档后续会介绍)。
如果你想保留这些 Spring Boot MVC 自定义设置并进行更多 MVC 自定义(拦截器、格式化器、视图控制器和其他特性),你可以添加自己的 @Configuration 类,类型为 WebMvcConfigurer,但不包含 @EnableWebMvc。
如果你想提供 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 或 ExceptionHandlerExceptionResolver 的自定义实例,并且仍然保留 Spring Boot MVC 的自定义设置,你可以声明一个 WebMvcRegistrations 类型的 bean,并使用它来提供这些组件的自定义实例。自定义实例将由 Spring MVC 进行进一步初始化和配置。要参与或在需要时覆盖后续处理,应使用 WebMvcConfigurer。
如果你不想使用自动配置并希望完全控制 Spring MVC,请添加带有 @EnableWebMvc 注解的 @Configuration 类。或者,如 @EnableWebMvc API 文档中所述,添加带有 @Configuration 注解的 DelegatingWebMvcConfiguration。
Spring MVC 转换服务
Spring MVC 使用的 ConversionService 与用于转换 application.properties 或 application.yaml 文件中值的 ConversionService 不同。这意味着 Period、Duration 和 DataSize 转换器不可用,并且 @DurationUnit 和 @DataSizeUnit 注解将被忽略。
如果你想自定义 Spring MVC 使用的 ConversionService,可以提供一个带有 addFormatters 方法的 WebMvcConfigurer bean。通过此方法,你可以注册所需的任何转换器,或者委托给 ApplicationConversionService 上可用的静态方法。
还可以使用 spring.mvc.format.* 配置属性来自定义转换。未配置时,使用以下默认值:
属性
日期时间格式化器
格式
spring.mvc.format.date
ofLocalizedDate(FormatStyle.SHORT)
java.util.Date 和 LocalDate
spring.mvc.format.time
ofLocalizedTime(FormatStyle.SHORT)
java.time 的 LocalTime 和 OffsetTime
spring.mvc.format.date-time
ofLocalizedDateTime(FormatStyle.SHORT)
java.time 的 LocalDateTime、OffsetDateTime 和 ZonedDateTime
HttpMessageConverters
Spring MVC 使用 HttpMessageConverter 接口来转换 HTTP 请求和响应。开箱即用地包含了合理的默认设置。例如,对象可以自动转换为 JSON(使用 Jackson 库)或 XML(如果 Jackson XML 扩展可用,则使用它;如果不可用,则使用 JAXB)。默认情况下,字符串使用 UTF-8 编码。
上下文中存在的任何 HttpMessageConverter bean 都会被添加到转换器列表中。你也可以以同样的方式覆盖默认转换器。
如果需要添加或自定义转换器,可以使用 Spring Boot 的 HttpMessageConverters 类,如下所示:
Java
Kotlin
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {
@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter> additional = new AdditionalHttpMessageConverter();
HttpMessageConverter> another = new AnotherHttpMessageConverter();
return new HttpMessageConverters(additional, another);
}
}
import org.springframework.boot.autoconfigure.http.HttpMessageConverters
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverter
@Configuration(proxyBeanMethods = false)
class MyHttpMessageConvertersConfiguration {
@Bean
fun customConverters(): HttpMessageConverters {
val additional: HttpMessageConverter<*> = AdditionalHttpMessageConverter()
val another: HttpMessageConverter<*> = AnotherHttpMessageConverter()
return HttpMessageConverters(additional, another)
}
}
为了进一步控制,你还可以继承 HttpMessageConverters 类并覆盖其 postProcessConverters 和/或 postProcessPartConverters 方法。当你想重新排序或移除 Spring MVC 默认配置的一些转换器时,这非常有用。
MessageCodesResolver
Spring MVC 有一套从绑定错误生成用于渲染错误消息的错误码的策略:MessageCodesResolver。如果你设置了 spring.mvc.message-codes-resolver-format 属性为 PREFIX_ERROR_CODE 或 POSTFIX_ERROR_CODE,Spring Boot 会为你创建一个(参见 DefaultMessageCodesResolver.Format 中的枚举)。
静态内容
默认情况下,Spring Boot 从 classpath 中名为 /static(或 /public、/resources 或 /META-INF/resources)的目录或从 ServletContext 的根目录提供静态内容。它使用 Spring MVC 的 ResourceHttpRequestHandler,因此你可以通过添加自己的 WebMvcConfigurer 并覆盖 addResourceHandlers 方法来修改该行为。
在独立 Web 应用程序中,容器的默认 servlet 未启用。可以使用 server.servlet.register-default-servlet 属性来启用它。
默认 servlet 作为备用,如果 Spring 决定不处理内容,则从 ServletContext 的根目录提供内容。大多数情况下,这不会发生(除非你修改了默认的 MVC 配置),因为 Spring 总是可以通过 DispatcherServlet 处理请求。
默认情况下,资源映射到 /**,但你可以使用 spring.mvc.static-path-pattern 属性进行调整。例如,可以将所有资源重定位到 /resources/**,如下所示:
Properties
YAML
spring.mvc.static-path-pattern=/resources/**
spring:
mvc:
static-path-pattern: "/resources/**"
你还可以使用 spring.web.resources.static-locations 属性来自定义静态资源位置(用目录位置列表替换默认值)。根 servlet 上下文路径 "/" 也会自动添加为位置。
除了前面提到的“标准”静态资源位置之外,对 Webjars 内容也做了特殊处理。默认情况下,路径在 /webjars/** 中的任何资源,如果以 Webjars 格式打包,都会从 jar 文件中提供。路径可以使用 spring.mvc.webjars-path-pattern 属性进行自定义。
如果你的应用程序打包为 jar,请勿使用 src/main/webapp 目录。尽管此目录是一个常见标准,但它仅适用于 war 打包,并且如果你生成 jar,大多数构建工具会默默忽略它。
Spring Boot 也支持 Spring MVC 提供的高级资源处理特性,例如允许使用缓存清除静态资源或使用与版本无关的 URL 来访问 Webjars。
要为 Webjars 使用与版本无关的 URL,请添加 org.webjars:webjars-locator-lite 依赖项。然后声明你的 Webjar。以 jQuery 为例,添加 "/webjars/jquery/jquery.min.js" 将生成 "/webjars/jquery/x.y.z/jquery.min.js",其中 x.y.z 是 Webjar 版本。
要使用缓存清除,以下配置为所有静态资源配置了一个缓存清除解决方案,有效地在 URL 中添加了内容哈希,例如 :
Properties
YAML
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
spring:
web:
resources:
chain:
strategy:
content:
enabled: true
paths: "/**"
借助为 Thymeleaf 和 FreeMarker 自动配置的 ResourceUrlEncodingFilter,模板中的资源链接会在运行时重写。使用 JSP 时,应手动声明此过滤器。其他模板引擎目前不支持自动配置,但可以通过自定义模板宏/助手和使用 ResourceUrlProvider 来实现。
例如,当使用 JavaScript 模块加载器动态加载资源时,重命名文件是不可行的。这就是为什么也支持其他策略并且可以组合使用的原因。“fixed” 策略在 URL 中添加一个静态版本字符串,而不改变文件名,如下例所示:
Properties
YAML
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
spring.web.resources.chain.strategy.fixed.enabled=true
spring.web.resources.chain.strategy.fixed.paths=/js/lib/
spring.web.resources.chain.strategy.fixed.version=v12
spring:
web:
resources:
chain:
strategy:
content:
enabled: true
paths: "/**"
fixed:
enabled: true
paths: "/js/lib/"
version: "v12"
使用此配置,位于 "/js/lib/" 下的 JavaScript 模块使用固定的版本策略("/v12/js/lib/mymodule.js"),而其他资源仍使用内容哈希版本()。
有关更多支持的选项,请参阅 WebProperties.Resources。
此特性已在专门的博客文章和 Spring Framework 的参考文档中详细描述。
欢迎页面
Spring Boot 支持静态和模板欢迎页面。它首先在配置的静态内容位置查找 index.html 文件。如果未找到,则查找 index 模板。如果找到其中之一,它会自动用作应用程序的欢迎页面。
这仅作为应用程序定义的实际索引路由的备用。顺序由 HandlerMapping bean 的顺序定义,默认情况下如下:
RouterFunctionMapping
使用 RouterFunction bean 声明的端点
RequestMappingHandlerMapping
在 @Controller bean 中声明的端点
WelcomePageHandlerMapping
欢迎页面支持
自定义 Favicon
与其他静态资源一样,Spring Boot 在配置的静态内容位置检查是否存在 favicon.ico 文件。如果存在此文件,它会自动用作应用程序的 favicon。
路径匹配与内容协商
Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射(例如,Controller 方法上的 @GetMapping 注解)匹配,来将传入的 HTTP 请求映射到处理程序。
Spring Boot 默认选择禁用后缀模式匹配,这意味着 "GET /projects/spring-boot.json" 等请求不会匹配到 @GetMapping("/projects/spring-boot") 映射。这被认为是 Spring MVC 应用程序的最佳实践。此功能在过去主要用于不发送正确 "Accept" 请求头的 HTTP 客户端;我们需要确保向客户端发送正确的 Content Type。如今,内容协商更加可靠。
还有其他方法来处理不持续发送正确 "Accept" 请求头的 HTTP 客户端。我们可以使用查询参数代替后缀匹配,以确保 "GET /projects/spring-boot?format=json" 等请求将被映射到 @GetMapping("/projects/spring-boot"):
Properties
YAML
spring.mvc.contentnegotiation.favor-parameter=true
spring:
mvc:
contentnegotiation:
favor-parameter: true
或者如果你喜欢使用不同的参数名称:
Properties
YAML
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
spring:
mvc:
contentnegotiation:
favor-parameter: true
parameter-name: "myparam"
大多数标准媒体类型开箱即用地受到支持,但你也可以定义新的类型:
Properties
YAML
spring.mvc.contentnegotiation.media-types.markdown=text/markdown
spring:
mvc:
contentnegotiation:
media-types:
markdown: "text/markdown"
从 Spring Framework 5.3 开始,Spring MVC 支持两种将请求路径匹配到控制器的策略。默认情况下,Spring Boot 使用 PathPatternParser 策略。PathPatternParser 是一个优化实现,但与 AntPathMatcher 策略相比存在一些限制。PathPatternParser 限制了某些路径模式变体的使用。它也与配置带有路径前缀(spring.mvc.servlet.path)的 DispatcherServlet 不兼容。
该策略可以使用 spring.mvc.pathmatch.matching-strategy 配置属性进行配置,如下例所示:
Properties
YAML
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
spring:
mvc:
pathmatch:
matching-strategy: "ant-path-matcher"
如果找不到请求的处理程序,Spring MVC 将抛出 NoHandlerFoundException。请注意,默认情况下,静态内容服务映射到 /**,因此将为所有请求提供处理程序。如果没有可用的静态内容,ResourceHttpRequestHandler 将抛出 NoResourceFoundException。要抛出 NoHandlerFoundException,请将 spring.mvc.static-path-pattern 设置为更具体的值(例如 /resources/**),或将 spring.web.resources.add-mappings 设置为 false 以完全禁用静态内容服务。
ConfigurableWebBindingInitializer
Spring MVC 使用 WebBindingInitializer 为特定请求初始化 WebDataBinder。如果你创建自己的 ConfigurableWebBindingInitializer @Bean,Spring Boot 会自动配置 Spring MVC 使用它。
模板引擎
除了 REST web 服务外,你还可以使用 Spring MVC 提供动态 HTML 内容。Spring MVC 支持各种模板技术,包括 Thymeleaf、FreeMarker 和 JSP。此外,许多其他模板引擎也包含自己的 Spring MVC 集成。
Spring Boot 包含对以下模板引擎的自动配置支持:
FreeMarker
Groovy
Thymeleaf
Mustache
如果可能,应避免使用 JSP。当与嵌入式 servlet 容器一起使用时,存在一些已知的限制。
当你使用这些模板引擎之一并采用默认配置时,你的模板会自动从 src/main/resources/templates 中加载。
根据你运行应用程序的方式,你的 IDE 可能会以不同的顺序排列 classpath。在 IDE 中从 main 方法运行应用程序的顺序与使用 Maven 或 Gradle 或从打包的 jar 运行时不同。这可能导致 Spring Boot 找不到预期的模板。如果遇到此问题,可以在 IDE 中重新排列 classpath,将模块的类和资源放在前面。
错误处理
默认情况下,Spring Boot 提供一个 /error 映射,以合理的方式处理所有错误,并将其注册为 servlet 容器中的“全局”错误页面。对于机器客户端,它会生成一个包含错误详情、HTTP 状态和异常消息的 JSON 响应。对于浏览器客户端,有一个“白标”错误视图,以 HTML 格式呈现相同的数据(要自定义它,请添加一个解析为 error 的 View)。
如果你想自定义默认的错误处理行为,可以设置一些 server.error 属性。请参见附录的Server Properties 部分。
要完全替换默认行为,你可以实现 ErrorController 并注册该类型的 bean 定义,或者添加一个 ErrorAttributes 类型的 bean 来使用现有机制但替换内容。
BasicErrorController 可以用作自定义 ErrorController 的基类。如果您想为新的内容类型添加处理程序(默认是专门处理 text/html 并为其他所有内容提供备用),这尤其有用。为此,请继承 BasicErrorController,添加一个带有 produces 属性的 @RequestMapping 公共方法,并创建您的新类型 bean。
从 Spring Framework 6.0 开始,支持 RFC 9457 Problem Details。Spring MVC 可以生成使用 application/problem+json 媒体类型的自定义错误消息,例如
{
"type": "https://example.org/problems/unknown-project",
"title": "Unknown project",
"status": 404,
"detail": "No project found for id 'spring-unknown'",
"instance": "/projects/spring-unknown"
}
可以通过将 spring.mvc.problemdetails.enabled 设置为 true 来启用此支持。
您还可以定义一个使用 @ControllerAdvice 注解的类,以自定义针对特定控制器和/或异常类型返回的 JSON 文档,如下例所示
Java
Kotlin
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice(basePackageClasses = SomeController.class)
public class MyControllerAdvice extends ResponseEntityExceptionHandler {
@ResponseBody
@ExceptionHandler(MyException.class)
public ResponseEntity> handleControllerException(HttpServletRequest request, Throwable ex) {
HttpStatus status = getStatus(request);
return new ResponseEntity<>(new MyErrorBody(status.value(), ex.getMessage()), status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer code = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
HttpStatus status = HttpStatus.resolve(code);
return (status != null) ? status : HttpStatus.INTERNAL_SERVER_ERROR;
}
}
import jakarta.servlet.RequestDispatcher
import jakarta.servlet.http.HttpServletRequest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
@ControllerAdvice(basePackageClasses = [SomeController::class])
class MyControllerAdvice : ResponseEntityExceptionHandler() {
@ResponseBody
@ExceptionHandler(MyException::class)
fun handleControllerException(request: HttpServletRequest, ex: Throwable): ResponseEntity<*> {
val status = getStatus(request)
return ResponseEntity(MyErrorBody(status.value(), ex.message), status)
}
private fun getStatus(request: HttpServletRequest): HttpStatus {
val code = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) as Int
val status = HttpStatus.resolve(code)
return status ?: HttpStatus.INTERNAL_SERVER_ERROR
}
}
在前面的示例中,如果由与 SomeController 在同一包中定义的控制器抛出 MyException,则使用 MyErrorBody POJO 的 JSON 表示形式,而不是 ErrorAttributes 表示形式。
在某些情况下,在控制器级别处理的错误不会被 Web 观测或 度量基础设施 记录。应用程序可以通过在观测上下文上设置已处理的异常来确保此类异常被观测记录下来。
自定义错误页面
如果您想为给定的状态码显示自定义 HTML 错误页面,可以将文件添加到 /error 目录。错误页面可以是静态 HTML(即,添加到任何静态资源目录下)或使用模板构建。文件名称应为精确的状态码或系列掩码。
例如,要将 404 映射到静态 HTML 文件,您的目录结构将如下所示
src/
+- main/
+- java/
| +
+- resources/
+- public/
+- error/
| +- 404.html
+-
要使用 FreeMarker 模板映射所有 5xx 错误,您的目录结构将如下所示
src/
+- main/
+- java/
| +
+- resources/
+- templates/
+- error/
| +- 5xx.ftlh
+-
对于更复杂的映射,您还可以添加实现 ErrorViewResolver 接口的 bean,如下例所示
Java
Kotlin
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.ModelAndView;
public class MyErrorViewResolver implements ErrorViewResolver {
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map
// Use the request or status to optionally return a ModelAndView
if (status == HttpStatus.INSUFFICIENT_STORAGE) {
// We could add custom model values here
new ModelAndView("myview");
}
return null;
}
}
import jakarta.servlet.http.HttpServletRequest
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver
import org.springframework.http.HttpStatus
import org.springframework.web.servlet.ModelAndView
class MyErrorViewResolver : ErrorViewResolver {
override fun resolveErrorView(request: HttpServletRequest, status: HttpStatus,
model: Map
// Use the request or status to optionally return a ModelAndView
if (status == HttpStatus.INSUFFICIENT_STORAGE) {
// We could add custom model values here
return ModelAndView("myview")
}
return null
}
}
您还可以使用常规的 Spring MVC 功能,例如 @ExceptionHandler 方法和 @ControllerAdvice。ErrorController 随后会捕获任何未处理的异常。
在 Spring MVC 之外映射错误页面
对于不使用 Spring MVC 的应用程序,可以使用 ErrorPageRegistrar 接口直接注册 ErrorPage 实例。此抽象直接与底层的嵌入式 Servlet 容器一起工作,即使您没有 Spring MVC DispatcherServlet 也能工作。
Java
Kotlin
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@Configuration(proxyBeanMethods = false)
public class MyErrorPagesConfiguration {
@Bean
public ErrorPageRegistrar errorPageRegistrar() {
return this::registerErrorPages;
}
private void registerErrorPages(ErrorPageRegistry registry) {
registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
}
}
import org.springframework.boot.web.server.ErrorPage
import org.springframework.boot.web.server.ErrorPageRegistrar
import org.springframework.boot.web.server.ErrorPageRegistry
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus
@Configuration(proxyBeanMethods = false)
class MyErrorPagesConfiguration {
@Bean
fun errorPageRegistrar(): ErrorPageRegistrar {
return ErrorPageRegistrar { registry: ErrorPageRegistry -> registerErrorPages(registry) }
}
private fun registerErrorPages(registry: ErrorPageRegistry) {
registry.addErrorPages(ErrorPage(HttpStatus.BAD_REQUEST, "/400"))
}
}
如果您注册一个 ErrorPage,其路径最终由 Filter 处理(这在 Jersey 和 Wicket 等一些非 Spring Web 框架中很常见),则必须将该 Filter 明确注册为 ERROR 分发器,如下例所示
Java
Kotlin
import java.util.EnumSet;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {
@Bean
public FilterRegistrationBean
FilterRegistrationBean
// ...
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
return registration;
}
}
import jakarta.servlet.DispatcherType
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.EnumSet
@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {
@Bean
fun myFilter(): FilterRegistrationBean
val registration = FilterRegistrationBean(MyFilter())
// ...
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType::class.java))
return registration
}
}
请注意,默认的 FilterRegistrationBean 不包含 ERROR 分发器类型。
WAR 部署中的错误处理
当部署到 Servlet 容器时,Spring Boot 使用其错误页面过滤器将带有错误状态的请求转发到相应的错误页面。这是必要的,因为 Servlet 规范不提供注册错误页面的 API。根据您部署 war 文件的容器以及应用程序使用的技术,可能需要一些额外的配置。
错误页面过滤器只有在响应尚未提交时才能将请求转发到正确的错误页面。默认情况下,WebSphere Application Server 8.0 及更高版本在 Servlet 的 service 方法成功完成时提交响应。您应通过将 com.ibm.ws.webcontainer.invokeFlushAfterService 设置为 false 来禁用此行为。
CORS 支持
跨源资源共享(CORS)是 W3C 规范,由大多数浏览器实现,它允许您灵活地指定授权哪些类型的跨域请求,而不是使用 IFRAME 或 JSONP 等不太安全且功能较弱的方法。
从 4.2 版本开始,Spring MVC 支持 CORS。在 Spring Boot 应用程序中使用带有 @CrossOrigin 注解的控制器方法 CORS 配置不需要任何特定配置。可以通过注册一个带有定制的 addCorsMappings(CorsRegistry) 方法的 WebMvcConfigurer bean 来定义全局 CORS 配置,如下例所示
Java
Kotlin
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration(proxyBeanMethods = false)
public class MyCorsConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**");
}
};
}
}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration(proxyBeanMethods = false)
class MyCorsConfiguration {
@Bean
fun corsConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/api/**")
}
}
}
}