Servlet Web 应用

如果你想构建基于servlet的网页应用,可以利用Spring Boot对Spring MVC或Jersey的自动配置功能。spring-doc.cadn.net.cn

“Spring Web MVC 框架”

Spring Web MVC 框架(通常称为“Spring MVC”)是一个丰富的“模型视图控制器”网络框架。 春季MVC让你创建特殊技能@Controller@RestController用来处理来电的HTTP请求。 控制器里的方法通过以下方式映射到 HTTP@RequestMapping附注。spring-doc.cadn.net.cn

以下代码显示了典型的@RestController该数据提供 JSON 数据:spring-doc.cadn.net.cn

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<Customer> getUserCustomers(@PathVariable Long userId) {
		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<Customer> {
		return userRepository.findById(userId).map(customerRepository::findByUser).get()
	}

	@DeleteMapping("/{userId}")
	fun deleteUser(@PathVariable userId: Long) {
		userRepository.deleteById(userId)
	}

}

“WebMvc.fn”功能变体将路由配置与实际请求处理分离,如下示例所示:spring-doc.cadn.net.cn

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<ServerResponse> routerFunction(MyUserHandler userHandler) {
		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<ServerResponse> {
		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)
	}

}
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 框架的一部分,详细信息可在参考文档中提供。 spring.io/guides 上也有几本涵盖春季MVC的指南。spring-doc.cadn.net.cn

你可以定义多少路由器功能豆子,你喜欢模块化路由器的定义。 如果需要优先顺序,可以订购Beans。

Spring MVC 自动配置

Spring Boot 为 Spring MVC 提供了自动配置,适用于大多数应用程序。 它取代了@EnableWebMvc而且两者不能同时使用。 除了 Spring MVC 的默认设置外,自动配置还提供以下功能:spring-doc.cadn.net.cn

如果你想保留那些 Spring Boot MVC 自定义,并做更多 MVC 自定义(拦截器、格式化器、视图控制器及其他功能),你可以添加自己的@Configuration类型类别WebMvcConfigurer没有 @EnableWebMvc.spring-doc.cadn.net.cn

如果你想提供自定义实例请求映射处理映射,RequestMappingHandlerAdapterExceptionHandlerExceptionResolver,同时保留Spring Boot MVC的自定义,你可以声明一个类型的豆子WebMvc注册并用它来提供这些组件的自定义实例。 自定义实例将由 Spring MVC 进一步初始化和配置。 为了参与并如愿可覆盖该后续处理,一个WebMvcConfigurer应该使用。spring-doc.cadn.net.cn

如果你不想使用自动配置,想完全控制 Spring MVC,可以自己添加@Configuration注释为@EnableWebMvc. 或者,你也可以添加自己的@Configuration-注释DelegatingWebMvcConfiguration@EnableWebMvcAPI 文档。spring-doc.cadn.net.cn

春季MVC转换服务

春季MVC使用不同的方式转换服务转换到用于将你的值转换的那个application.propertiesapplication.yaml文件。 意思是时期,期间数据大小没有转换器,且@DurationUnit@DataSizeUnit注释将被忽略。spring-doc.cadn.net.cn

如果你想自定义转换服务Spring MVC使用的话,你可以提供WebMvcConfigurer带有addFormatters方法。 通过这种方法,你可以注册任何你喜欢的转换器,或者你可以委派给 Statics 方法。ApplicationConversionService.spring-doc.cadn.net.cn

转换也可以通过以下方式进行定制spring.mvc.format.*配置属性。 未配置时,使用以下默认值:spring-doc.cadn.net.cn

属性 DateTimeFormatter 格式

spring.mvc.format.datespring-doc.cadn.net.cn

ofLocalizedDate(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.util.Date本地日期spring-doc.cadn.net.cn

spring.mvc.format.timespring-doc.cadn.net.cn

ofLocalizedTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.time的本地时间偏移时间spring-doc.cadn.net.cn

spring.mvc.format.date-timespring-doc.cadn.net.cn

ofLocalizedDateTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.time的本地日期时间,偏移日期时间ZonedDateTimespring-doc.cadn.net.cn

HttpMessage转换器

春季MVC使用以下HttpMessage转换器用于转换HTTP请求和响应的接口。 默认默认设置是开箱即用的。 例如,对象可以自动转换为JSON(使用Jackson库)或XML(如果有Jackson XML扩展,或如果没有Jackson XML扩展则使用JAXB)。 默认情况下,字符串编码为UTF-8.spring-doc.cadn.net.cn

任何HttpMessage转换器上下文中存在的豆子会被加入转换器列表。 你也可以用同样的方式覆盖默认转换器。spring-doc.cadn.net.cn

如果你需要添加或自定义转换器,可以申报一个或多个客户端HttpMessageConverters定制器ServerHttpMessageConvertersCustomizer如以下列表所示:spring-doc.cadn.net.cn

import org.springframework.boot.http.converter.autoconfigure.ClientHttpMessageConvertersCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {

	@Bean
	public ClientHttpMessageConvertersCustomizer myClientConvertersCustomizer() {
		return (clientBuilder) -> clientBuilder.addCustomConverter(new AdditionalHttpMessageConverter())
			.addCustomConverter(new AnotherHttpMessageConverter());
	}

}
import org.springframework.boot.http.converter.autoconfigure.ClientHttpMessageConvertersCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverters

@Configuration(proxyBeanMethods = false)
class MyHttpMessageConvertersConfiguration {

	@Bean
	fun myClientConvertersCustomizer(): ClientHttpMessageConvertersCustomizer {
		return ClientHttpMessageConvertersCustomizer { clientBuilder: HttpMessageConverters.ClientBuilder ->
			clientBuilder
				.addCustomConverter(AdditionalHttpMessageConverter())
				.addCustomConverter(AnotherHttpMessageConverter())
		}
	}

}

MessageCodesResolver

Spring MVC 有一种策略用于生成错误代码,用于从绑定错误中渲染错误消息:MessageCodesResolver. 如果你设置spring.mvc.message-codes-resolver-format属性PREFIX_ERROR_CODEPOSTFIX_ERROR_CODE,Spring Boot 为你创建一个(参见DefaultMessageCodesResolver.Format).spring-doc.cadn.net.cn

静态内容

默认情况下,Spring Boot 从名为/静态的(或/公共/资源/元补强/资源)在类路径中或从ServletContext. 它使用ResourceHttpRequestHandler从Spring MVC中删除,这样你可以通过添加自己的行为来修改该行为WebMvcConfigurer并且覆盖addResourceHandlers方法。spring-doc.cadn.net.cn

在独立的网页应用中,容器中的默认 servlet 不会被启用。 可以通过以下方式启用server.servlet.register-default-servlet财产。spring-doc.cadn.net.cn

默认的servlet作为后备,从根节点提供内容ServletContext如果Spring决定不处理的话。 大多数情况下,这种情况不会发生(除非你修改默认的MVC配置),因为Spring总能通过调度器服务.spring-doc.cadn.net.cn

默认情况下,资源映射在 ,但你可以用/**spring.mvc.static-path-pattern财产。 例如,将所有资源重新定位到/资源/**可以实现如下:spring-doc.cadn.net.cn

spring.mvc.static-path-pattern=/resources/**
spring:
  mvc:
    static-path-pattern: "/resources/**"

你也可以通过使用spring.web.resources.static-locations属性(用目录位置列表替换默认值)。 根 servlet 上下文路径 也会自动作为位置添加。"/"spring-doc.cadn.net.cn

除了前面提到的“标准”静态资源位置外,Webjars 内容还有一个特殊情况。 默认情况下,任何路径在 的资源/webjars/ **如果 jar 文件以 Webjars 格式打包,则 是从中提供。 路径可以通过以下方式进行自定义Spring.mvc.webjars-path-pattern财产。spring-doc.cadn.net.cn

不要使用src/main/webapp如果你的应用是打包成jar的,目录。 虽然该目录是通用标准,但它适用于战争包装,且大多数构建工具在生成jar时会默默认忽略。

Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用如缓存破坏静态资源或使用版本无关的 Webjar URL 等用例。spring-doc.cadn.net.cn

要使用版本无关的Webjar URL,请添加org.webjars:webjars-locator-liteDependency。 然后声明你的Webjar。 以jQuery为例,添加“/webjars/jquery/jquery.min.js”结果如下“/webjars/jquery/x.y.z/jquery.min.js”哪里X.Y.Z是Webjar版本。spring-doc.cadn.net.cn

要使用缓存破坏,以下配置为所有静态资源配置缓存破坏解决方案,实际上增加了内容哈希,例如<link href=“/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css”/>,在URL中:spring-doc.cadn.net.cn

spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
spring:
  web:
    resources:
      chain:
        strategy:
          content:
            enabled: true
            paths: "/**"
资源链接在运行时会在模板中重写,这得益于ResourceUrlEncodingFilter该系统自动配置为Thymeleaf 和 FreeMarker。 使用JSP时应手动声明这个过滤器。 其他模板引擎目前不自动支持,但可以通过自定义模板宏/辅助工具和使用ResourceUrlProvider.

例如,在使用JavaScript模块加载器动态加载资源时,无法重命名文件。 这就是为什么其他策略也被支持并可以组合使用。 “固定”策略是在不更改文件名的情况下,在URL中添加静态版本字符串,如下示例所示:spring-doc.cadn.net.cn

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"

在这种配置下,JavaScript 模块位于“/js/lib/”使用固定版本控制策略(“/v12/js/lib/mymodule.js”),而其他资源仍使用内容资源(<link href=“/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css”/>).spring-doc.cadn.net.cn

WebProperties.资源更多支持的选项。spring-doc.cadn.net.cn

这一功能在专门的博客文章和 Spring Framework 的参考文档中都有详细描述。spring-doc.cadn.net.cn

欢迎页

Spring Boot 支持静态和模板欢迎页。 它首先寻找一个index.html文件位于配置好的静态内容位置。 如果找不到,则会寻找指数模板。 如果找到其中任何一个,它会自动作为申请的欢迎页面使用。spring-doc.cadn.net.cn

这仅作为应用程序实际定义的索引路由的后备。 该序由 的阶定义处理器映射豆子默认如下:spring-doc.cadn.net.cn

RouterFunctionMappingspring-doc.cadn.net.cn

端点声明为路由器功能spring-doc.cadn.net.cn

请求映射处理映射spring-doc.cadn.net.cn

@Controllerspring-doc.cadn.net.cn

欢迎页面处理映射spring-doc.cadn.net.cn

欢迎页面支持spring-doc.cadn.net.cn

定制 Favicon

与其他静态资源一样,Spring Boot 会检查favicon.ico在配置好的静态内容位置。 如果存在这样的文件,它会自动作为应用程序的动态编辑器使用。spring-doc.cadn.net.cn

路径匹配与内容协商

Spring MVC 可以通过查看请求路径并将其与你应用程序中定义的映射匹配,将收到的 HTTP 请求映射到处理器上(例如,@GetMapping控制器方法的注释)。spring-doc.cadn.net.cn

Spring Boot 默认选择禁用后缀模式匹配,这意味着“获取/项目/spring-boot.json”不会匹配到@GetMapping(“/projects/spring-boot”)映射。 这被视为春季MVC应用的最佳实践。 这一功能过去主要适用于未发送正确“接受”请求头的 HTTP 客户端;我们需要确保向客户发送正确的内容类型。 如今,内容协商更加可靠。spring-doc.cadn.net.cn

还有其他方法可以处理那些不稳定发送正确“接受”请求头的 HTTP 客户端。 我们可以用查询参数来确保请求“GET /projects/spring-boot?format=json”将映射到@GetMapping(“/projects/spring-boot”):spring-doc.cadn.net.cn

spring.mvc.contentnegotiation.favor-parameter=true
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true

或者如果你喜欢用不同的参数名:spring-doc.cadn.net.cn

spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: "myparam"

大多数标准媒体类型开箱即用,但你也可以定义新的:spring-doc.cadn.net.cn

spring.mvc.contentnegotiation.media-types.markdown=text/markdown
spring:
  mvc:
    contentnegotiation:
      media-types:
        markdown: "text/markdown"

截至 Spring Framework 5.3,Spring MVC 支持两种将请求路径匹配到控制器的策略。 默认情况下,Spring Boot 使用路径模式解析器策略。路径模式解析器优化实现,但相比蚁径匹配器策略。路径模式解析器限制某些路径模式变体的使用。 它也无法与配置调度器服务带有路径前缀(spring.mvc.servlet.path).spring-doc.cadn.net.cn

该策略可以通过以下方式进行配置spring.mvc.pathmatch.matching-strategy配置性质,如下示例所示:spring-doc.cadn.net.cn

spring.mvc.pathmatch.matching-strategy=ant-path-matcher
spring:
  mvc:
    pathmatch:
      matching-strategy: "ant-path-matcher"

春季MVC将举行未处理者发现异常如果请求时找不到handler。 请注意,默认情况下,静态内容的提供映射为 ,因此会为所有请求提供一个处理程序。 如果没有静态内容,/**ResourceHttpRequestHandler将抛出NoResourceFoundException. 对于一个未处理者发现异常要被扔,设spring.mvc.static-path-pattern变为更具体的值,例如/资源/**或集合spring.web.resources.add-mappingsfalse完全禁用静态内容的提供。spring-doc.cadn.net.cn

ConfigurableWebBindingInitializer

Spring MVC 使用WebBindingInitializer以初始化一个WebDataBinder针对某个特定请求。如果你自己创建ConfigurableWebBindingInitializer @BeanSpring Boot 会自动配置 Spring MVC 以使用该功能。spring-doc.cadn.net.cn

模板引擎

除了 REST Web 服务,你还可以使用 Spring MVC 来提供动态的 HTML 内容。Spring MVC 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 JSP。此外,许多其他模板引擎也包含自己的 Spring MVC 集成。spring-doc.cadn.net.cn

Spring Boot 支持以下模板引擎的自动配置:spring-doc.cadn.net.cn

如果可能,应避免使用 JSP。在与嵌入式 servlet 容器一起使用时,存在一些已知的限制

当你使用这些模板引擎中的默认配置时,模板会自动从以下获取src/main/resources/模板.spring-doc.cadn.net.cn

根据你运行应用的方式,IDE 可能会对类路径的排序不同。在 IDE 中用主方法运行应用,顺序与使用 Maven、Gradle 或其打包的 jar 运行时不同。这可能导致 Spring Boot 找不到预期的模板。如果你遇到这个问题,可以在 IDE 中重新排序类路径,将模块的类和资源放在前面。

错误处理

默认情况下,Spring Boot 提供/错误映射以合理方式处理所有错误,并在 servlet 容器中注册为“全局”错误页面。对于机器客户端,它生成包含错误详细信息、HTTP 状态和异常消息的 JSON 响应。对于浏览器客户端,有一个“白标”错误视图,将相同数据渲染成 HTML 格式(如需自定义,需添加视图该 结算为错误).spring-doc.cadn.net.cn

有若干server.error如果你想自定义默认的错误处理行为,可以设置这些属性。请参见附录中的服务器属性部分。spring-doc.cadn.net.cn

要完全替换默认行为,你可以实现错误控制器并注册该类型的豆定义,或添加一个类型的豆错误属性使用现有机制但替换内容物。spring-doc.cadn.net.cn

基础错误控制器可以作为自定义的基类使用错误控制器. 如果你想为新内容类型添加处理程序(默认是处理),这尤其有用文本/HTML具体来说,并为其他所有事情提供备选方案)。为此,可以延长基础错误控制器,添加一个具有@RequestMapping生产属性,然后创建你新类型的豆子。

自 Spring Framework 6.0 起,支持 RFC 9457 问题详情。Spring MVC 可以生成自定义错误消息,包含application/problem+json媒体类型,比如:spring-doc.cadn.net.cn

{
	"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.enabledtrue.spring-doc.cadn.net.cn

你也可以定义一个注释为@ControllerAdvice自定义 JSON 文档以返回特定控制器和/或异常类型,如下示例所示:spring-doc.cadn.net.cn

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
	}

}

在前述例子中,如果我的例外由定义在同一包中某个控制者,一个 JSON 表示我的错误身体使用 POJO 代替错误属性表示法。spring-doc.cadn.net.cn

在某些情况下,控制器级别处理的错误不会被网络观测或度量基础设施记录。应用程序可以通过在观测上下文中设置处理异常,确保这些异常与观测值一起被记录。spring-doc.cadn.net.cn

自定义错误页面

如果你想为某个状态码显示自定义的 HTML 错误页面,你可以向/错误目录。 错误页面可以是静态HTML(即添加在任何静态资源目录下),也可以使用模板构建。文件名称应为准确的状态码或系列掩码。spring-doc.cadn.net.cn

例如,映射404对于静态HTML文件,你的目录结构如下:spring-doc.cadn.net.cn

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

映射所有5xx错误,使用 FreeMarker 模板时,你的目录结构如下:spring-doc.cadn.net.cn

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftlh
             +- <other templates>

对于更复杂的映射,你还可以添加实现ErrorViewResolver如下示例所示的接口:spring-doc.cadn.net.cn

import java.util.Map;

import jakarta.servlet.http.HttpServletRequest;

import org.springframework.boot.webmvc.autoconfigure.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<String, Object> model) {
		// 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.webmvc.autoconfigure.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<String, Any>): ModelAndView? {
		// 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. 这错误控制器然后拾取任何未处理的异常。spring-doc.cadn.net.cn

Spring MVC 以外的映射错误页面

对于不使用 Spring MVC 的应用程序,可以使用ErrorPage注册商直接注册的接口错误页面实例。 这种抽象直接与底层嵌入的 servlet 容器工作,即使没有 Spring MVC 也能运行调度器服务.spring-doc.cadn.net.cn

import org.springframework.boot.web.error.ErrorPage;
import org.springframework.boot.web.error.ErrorPageRegistrar;
import org.springframework.boot.web.error.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.error.ErrorPage
import org.springframework.boot.web.error.ErrorPageRegistrar
import org.springframework.boot.web.error.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"))
	}

}
如果你注册了错误页面其中路径最终由Filter(这在一些非 Spring 的 Web 框架中很常见,比如 Jersey 和 Wicket),然后Filter必须明确注册为错误调度员,如下例所示:
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<MyFilter> myFilter() {
		FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(new MyFilter());
		// ...
		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<MyFilter> {
		val registration = FilterRegistrationBean(MyFilter())
		// ...
		registration.setDispatcherTypes(EnumSet.allOf(DispatcherType::class.java))
		return registration
	}

}

注意默认情况下FilterRegistrationBean不包括错误调度员类型。spring-doc.cadn.net.cn

WAR部署中的错误处理

当部署到servlet容器时,Spring Boot会使用错误页面过滤器将带有错误状态的请求转发到相应的错误页面。 这是必要的,因为servlet规范没有提供注册错误页的API。 根据你部署战争文件的容器和应用所用技术,可能需要一些额外的配置。spring-doc.cadn.net.cn

错误页面过滤器只能在响应尚未提交时将请求转发到正确的错误页面。 默认情况下,WebSphere Application Server 8.0及以后版本在成功完成servlet服务方法时提交响应。 你应该通过设置来禁用这种行为com.ibm.ws.webcontainer.invokeFlushAfterServicefalse.spring-doc.cadn.net.cn

CORS支持系统

跨源资源共享(CORS)是W3C规范,大多数浏览器实现它允许灵活地指定授权的跨域请求类型,而不是使用一些安全性较低、功能较弱的方法,如IFRAME或JSONP。spring-doc.cadn.net.cn

从4.2版本起,Spring MVC支持CORS。 使用控制器方法,CORS配置@CrossOriginSpring Boot 应用中的注释不需要特定的配置。全局CORS配置可以通过注册WebMvcConfigurer带有定制的豆子addCorsMappings(CorsRegistry)如下例所示:spring-doc.cadn.net.cn

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/**")
			}
		}
	}

}

API 版本管理

Spring MVC 支持 API 版本管理,可以用来随着时间演进 HTTP API。 一样@Controller路径可以多次映射以支持不同版本的 API。spring-doc.cadn.net.cn

新增了映射功能,你还需要配置 Spring MVC,使其能够使用随请求发送的任何版本信息。 通常,版本以HTTP头、查询参数或路径的一部分形式发送。spring-doc.cadn.net.cn

要配置 Spring MVC,你可以使用WebMvcConfigurer豆子并覆盖configureApiVersioning(...)方法,或者你可以使用属性。spring-doc.cadn.net.cn

例如,以下将使用一个X版本HTTP 头部用于获取版本信息,默认为1.0.0当没有发送任何标题时。spring-doc.cadn.net.cn

spring.mvc.apiversion.default=1.0.0
spring.mvc.apiversion.use.header=X-Version
spring:
  mvc:
    apiversion:
      default: 1.0.0
      use:
        header: X-Version

为了更全面的控制,你也可以定义ApiVersionResolver,ApiVersionParserApiVersionDeprecationHandler这些豆子将被注入自动配置的Spring MVC配置中。spring-doc.cadn.net.cn

API版本管理也支持两者Web客户端Rest客户端. 详情请参见 API 版本管理

JAX-RS和Jersey

如果你更喜欢 JAX-RS 编程模型用于 REST 端点,可以使用现有的实现之一,而不是 Spring MVC。JerseyApache CXF开箱即用效果很好。 CXF要求你注册其servletFilter作为@Bean在你的应用背景下。 Jersey 有一些原生的 Spring 支持,所以我们在 Spring Boot 中也提供了自动配置支持,并附带一个启动程序。spring-doc.cadn.net.cn

要开始使用Jersey,请包含春季靴-首发球衣作为依赖,然后你需要一个@Bean类型ResourceConfig你注册所有端点,如下示例所示:spring-doc.cadn.net.cn

import org.glassfish.jersey.server.ResourceConfig;

import org.springframework.stereotype.Component;

@Component
public class MyJerseyConfig extends ResourceConfig {

	public MyJerseyConfig() {
		register(MyEndpoint.class);
	}

}
Jersey对扫描可执行档案的支持相当有限。 例如,它无法扫描包中完全可执行的 jar 文件中的端点,或者网络基础/课程运行可执行的战争文件时。 为避免此限制,以下不应使用该方法,端点应通过使用注册如前例所示。

对于更高级的自定义,你也可以注册任意数量的豆子来实现ResourceConfigCustomizer.spring-doc.cadn.net.cn

所有注册端点都应该是@Component带有 HTTP 资源注释(@GET以及其他),如下例所示:spring-doc.cadn.net.cn

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.springframework.stereotype.Component;

@Component
@Path("/hello")
public class MyEndpoint {

	@GET
	public String message() {
		return "Hello";
	}

}

自从......@Endpoint是泉水@Component,其生命周期由Spring管理,你可以使用@Autowired注释以注入依赖关系并使用@Value注释以注入外部配置。 默认情况下,Jerseyservlet被注册并映射到。 你可以通过添加/*@ApplicationPath给你的ResourceConfig.spring-doc.cadn.net.cn

默认情况下,Jersey被设置为@Bean类型ServletRegistrationBean球衣Servlet注册. 默认情况下,servlet是懒惰初始化的,但你可以通过设置来自定义这个行为spring.jersey.servlet.load-on-startup. 你可以通过创建同名的自己豆子来禁用或覆盖那个豆子。 你也可以用Filter代替servlet,方法是设置spring.jersey.type=filter(在这种情况下,@Bean替换或覆盖为jerseyFilter注册). Filter有一个@Order,你可以用 来设置春季.Jersey.过滤器.订单. 使用 Jersey 作为过滤器时,必须有一个 servlet 来处理 Jersey 未拦截的请求。 如果您的应用程序不包含这样的 servlet,您可能想通过设置来启用默认 servlet。server.servlet.register-default-servlettrue. servlet 和Filter注册都可以通过以下方式给定初始化参数春季。Jersey.init.*以指定性质映射。spring-doc.cadn.net.cn

嵌入式 Servlet 容器支持

对于 servlet 应用,Spring Boot 支持嵌入式 TomcatJetty 服务器。 大多数开发者会使用相应的起始程序来获得一个完全配置好的实例。 默认情况下,嵌入式服务器会监听端口上的HTTP请求8080.spring-doc.cadn.net.cn

Servlet、Filter与监听器

使用嵌入式 servlet 容器时,你可以注册 servlet、filter,以及所有监听器(例如HttpSessionListener)通过使用 Spring 豆子或扫描 servlet 组件,从服务器规范中提取。spring-doc.cadn.net.cn

注册servlets、Filter和监听器作为春豆

任何servlet,Filter,或servlet。*听者实例 是 Spring Bean 的实例被注册在嵌入的容器中。 如果你想引用 Mine 的某个值,这尤其方便application.properties在配置过程中。spring-doc.cadn.net.cn

默认情况下,如果上下文仅包含一个 Servlet,则映射到 。 对于多个servlet豆,豆名作为路径前缀使用。 筛选器映射到。//*spring-doc.cadn.net.cn

如果基于约定的映射不够灵活,你可以使用ServletRegistrationBean,FilterRegistrationBeanServletListenerRegistrationBean为了完全控制。 如果你更喜欢注释而不是注释ServletRegistrationBeanFilterRegistrationBean,你也可以使用@ServletRegistration@FilterRegistration作为替代方案。spring-doc.cadn.net.cn

过滤豆通常可以不按顺序放置。 如果需要特定订单,你应该在Filter@Order或者让它实现命令. 你无法配置 的顺序Filter通过对其豆子方法进行注释,记入@Order. 如果你无法更改Filter需要添加的类别@Order或者实现命令,你必须定义一个FilterRegistrationBean对于Filter并用setOrder(int)方法。 或者,如果你喜欢注释,也可以使用@FilterRegistration并设置次序属性。 避免配置一个读取请求主体的过滤器Ordered.HIGHEST_PRECEDENCE因为这可能违背你应用的字符编码配置。 如果 servlet 过滤器包裹请求,应配置其顺序小于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER.spring-doc.cadn.net.cn

查看每个的顺序Filter在您的应用程序中,启用调试级别日志Web Logging组 (logging.level.web=调试). 注册过滤器的详细信息,包括顺序和URL模式,将在启动时被记录。
注册时请小心Filter因为它们在应用生命周期的早期就被初始化。 如果你需要注册Filter如果会与其他豆子相互作用,可以考虑使用DelegatingFilterProxyRegistrationBean相反。

Servlet 上下文初始化

嵌入的servlet容器不会直接执行ServletContainerInitializer界面或Spring的WebApplicationInitializer接口。 这是一个有意设计的决策,旨在降低第三方库在战争中运行时可能破坏 Spring Boot 应用的风险。spring-doc.cadn.net.cn

如果你需要在 Spring Boot 应用中执行 servlet 上下文初始化,你应该注册一个实现ServletContextInitializer接口。 单曲onStartup方法提供访问ServletContext如有必要,也可以轻松作为现有WebApplicationInitializer.spring-doc.cadn.net.cn

初始化参数

init 参数可以在ServletContextserver.servlet.context-parameters.*性能。 例如,性质server.servlet.context-parameters.com.example.parameter=example将配置ServletContext名为com.example.parameter其中值为示例.spring-doc.cadn.net.cn

扫描 Servlets、Filter和听众

使用嵌入式容器时,自动注册类,注释为@WebServlet,@WebFilter@WebListener可以通过以下方式来启用@ServletComponentScan.spring-doc.cadn.net.cn

@ServletComponentScan在独立容器中则无效,因为容器内置的发现机制被使用。

The ServletWebServerApplicationContext

在引擎盖底下,Spring Boot采用了另一种应用上下文用于嵌入式 Servlet 容器支持。 这ServletWebServerApplicationContext是一种特殊类型的WebApplicationContext它通过寻找单一的ServletWebServerFactory豆。 通常是TomcatServletWebServerFactoryJettyServletWebServerFactory已被自动配置。spring-doc.cadn.net.cn

你通常不需要了解这些实现类。 大多数应用程序都是自动配置的,并且需要相应配置应用上下文ServletWebServerFactory是代表你创建的。

在嵌入式容器设置中,ServletContext作为服务器启动的一部分,启动发生在应用上下文初始化期间。 因为这个豆子在应用上下文无法可靠地用ServletContext. 解决这个问题的一种方法是注射应用上下文作为豆子和访问的依赖ServletContext只有在需要的时候才会这样。 另一种方法是服务器启动后使用回调。 这可以通过ApplicationListener该系统监听ApplicationStartedEvent如下:spring-doc.cadn.net.cn

import jakarta.servlet.ServletContext;

import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.WebApplicationContext;

public class MyDemoBean implements ApplicationListener<ApplicationStartedEvent> {

	private ServletContext servletContext;

	@Override
	public void onApplicationEvent(ApplicationStartedEvent event) {
		ApplicationContext applicationContext = event.getApplicationContext();
		this.servletContext = ((WebApplicationContext) applicationContext).getServletContext();
	}

}

定制嵌入式 Servlet 容器

常见的servlet容器设置可以通过Spring进行配置环境性能。 通常,你会定义application.propertiesapplication.yaml文件。spring-doc.cadn.net.cn

常见的服务器设置包括:spring-doc.cadn.net.cn

Spring Boot 尽力暴露常见设置,但这并不总是可行。 对于这些情况,专用命名空间提供服务器特定的自定义(参见服务器.tomcat). 例如,访问日志可以配置嵌入 servlet 容器的特定功能。spring-doc.cadn.net.cn

参见ServerProperties完整列表。

SameSite Cookies

同站Cookie属性可以被网页浏览器用来控制Cookie是否以及如何提交跨站请求。 该属性对于现代网页浏览器尤为重要,因为它们开始更改该属性缺失时使用的默认值。spring-doc.cadn.net.cn

如果你想更改同站你的会话Cookie属性,你可以使用server.servlet.session.cookie.same-site财产。 该特性被自动配置的Tomcat和Jetty服务器支持。 它也用于基于 Spring Session servlet 的配置会话仓库豆。spring-doc.cadn.net.cn

例如,如果你希望你的会话Cookie拥有同站属性没有,你可以在你的application.propertiesapplication.yaml文件:spring-doc.cadn.net.cn

server.servlet.session.cookie.same-site=none
server:
  servlet:
    session:
      cookie:
        same-site: "none"

如果你想更改同站添加到您的其他Cookie上,属性HttpServletResponse(服务服务响应),你可以用CookieSameSiteSupplier. 这CookieSameSiteSupplier经过a饼干并且可以返回同站值,或.spring-doc.cadn.net.cn

有许多便利工厂和过滤方法可以快速匹配特定cookie。 例如,添加以下豆子会自动应用同站宽松对于所有名称与正则表达式相符的 Cookie我的应用。*.spring-doc.cadn.net.cn

import org.springframework.boot.web.server.servlet.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MySameSiteConfiguration {

	@Bean
	public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
		return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*");
	}

}
import org.springframework.boot.web.server.servlet.CookieSameSiteSupplier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MySameSiteConfiguration {

	@Bean
	fun applicationCookieSameSiteSupplier(): CookieSameSiteSupplier {
		return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*")
	}

}

字符编码

嵌入式服务容器用于请求和响应处理的字符编码行为可以通过以下方式配置server.servlet.encoding.*配置属性。spring-doc.cadn.net.cn

当请求接受语言头 表示请求的所在地,servlet 容器会自动将其映射到一个字符集。 每个容器都会默认为字符集映射提供位置,你应该核实它们是否满足你的应用程序需求。 如果没有,可以使用server.servlet.encoding.mapping配置属性用于自定义映射,如下示例所示:spring-doc.cadn.net.cn

server.servlet.encoding.mapping.ko=UTF-8
server:
  servlet:
    encoding:
      mapping:
        ko: "UTF-8"

在前面的例子中,Ko(韩语)地点已映射为UTF-8. 这等价于<locale-encoding-mapping-list>web.xml传统战争部署的档案。spring-doc.cadn.net.cn

程序化定制

如果你需要程序化配置嵌入式 servlet 容器,可以注册一个实现WebServerFactoryCustomizer接口。WebServerFactoryCustomizer提供访问ConfigurableServletWebServerFactory其中包含多种自定义设置器方法。 以下示例展示了程序化设置端口:spring-doc.cadn.net.cn

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

	@Override
	public void customize(ConfigurableServletWebServerFactory server) {
		server.setPort(9000);
	}

}
import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component

@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

	override fun customize(server: ConfigurableServletWebServerFactory) {
		server.setPort(9000)
	}

}

TomcatServletWebServerFactoryJettyServletWebServerFactory是 的专用变体ConfigurableServletWebServerFactory分别为Tomcat和Jetty提供了额外的自定义设置方法。 以下示例展示了如何自定义TomcatServletWebServerFactory提供针对Tomcat的专属配置选项:spring-doc.cadn.net.cn

import java.time.Duration;

import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

	@Override
	public void customize(TomcatServletWebServerFactory server) {
		server.addConnectorCustomizers((connector) -> connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
	}

}
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory
import org.springframework.stereotype.Component
import java.time.Duration

@Component
class MyTomcatWebServerFactoryCustomizer : WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

	override fun customize(server: TomcatServletWebServerFactory) {
		server.addConnectorCustomizers({ connector -> connector.asyncTimeout = Duration.ofSeconds(20).toMillis() })
	}

}

自定义 ConfigurableServletWebServerFactory 直接

对于需要扩展的高级用例ServletWebServerFactory你自己可以暴露出这种类型的豆子。spring-doc.cadn.net.cn

设置杆提供多种配置选项。 如果你想做一些更特别的事情,游戏还提供了几个安全的“钩子”。 参见ConfigurableServletWebServerFactory详细信息请参考API文档。spring-doc.cadn.net.cn

自动配置的自定义工具仍然会应用到你的自定义工厂,所以要谨慎使用这个选项。

JSP的局限性

当运行使用嵌入式 servlet 容器(并以可执行档案形式打包)的 Spring Boot 应用时,JSP 支持存在一些限制。spring-doc.cadn.net.cn

  • 对于Jetty和Tomcat,如果你用战争包装,应该能用。 可执行的战争在启动时是可行的Java -jar,并且可以部署到任何标准容器上。 使用可执行 jar 时不支持 JSP。spring-doc.cadn.net.cn

  • 创建自定义error.jsp页面不会覆盖默认视图以处理错误。应使用自定义错误页面spring-doc.cadn.net.cn