Skip to content

29.开发web应用

大部分web应用使用 spring-boot-starter-web 就够了。也可以使用 spring-boot-starter-webflux 构建响应式的web应用。

1. Spring MVC 自动配置

在Spring默认功能上添加了以下特性:

  • Inclusion of(引入) ContentNegotiatingViewResolver and BeanNameViewResolver beans.
  • Support for serving static resources, including support for WebJars (covered later in this document)).
  • Automatic registration of Converter, GenericConverter, and Formatter beans.
  • Support for HttpMessageConverters (covered later in this document).
  • Automatic registration of MessageCodesResolver (covered later in this document).
  • Static index.html support.
  • Custom Favicon support (covered later in this document).
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

如果想添加额外的 MVC configuration (interceptors, formatters, view controllers, and other features),可以添加 WebMvcConfigurer 类型的@Configuration 的类并不要有 @EnableWebMvc。如果想要添加自定义的RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver,可以声明一个WebMvcRegistrationsAdapter 类型的。

如果想要完全的控制Spring MVC,在@Configuration 类上添加@EnableWebMvc注解。

2. HttpMessageConverters

Spring MVC使用该接口转换HTTP requests和responses。

默认情况下字符串使用UTF-8编码。

使用Spring Boot的HttpMessageConverters类添加或定制转换器:

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration
public class MyConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = ...
        HttpMessageConverter<?> another = ...
        return new HttpMessageConverters(additional, another);
    }

}

在上下文中存在的任何转换器都会被加入到转换器列表中。也可通过此方式覆盖默认的转换器。

3. 自定义JSON 序列化和反序列化

适用于Jackson。直接在 JsonSerializer or JsonDeserializer 上使用@JsonComponent,也可以用在将两者作为内部类的类:

import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;

@JsonComponent
public class Example {

    public static class Serializer extends JsonSerializer<SomeObject> {
        // ...
    }

    public static class Deserializer extends JsonDeserializer<SomeObject> {
        // ...
    }

}

4. MessageCodesResolver

TODO

Spring MVC有一组生成错误码的策略,用于从绑定错误中呈现错误消息:MessageCodesResolver。

设置spring.mvc.message-codes-resolver.format 属性PREFIX_ERROR_CODE or POSTFIX_ERROR_CODE,Spring Boot会给你创建一个。

5. 错误处理

Spring Boot默认提供一个/error映射处理所有的错误。对于浏览器,是一个html格式的whitelabel错误视图。替换默认行为有两种方式:

  • 实现ErrorController并注册成bean完全地改变该行为
  • 添加ErrorAttributes 类型的bean替换掉内容

还可以使用@ControllerAdvice 注解定义一个类来为一个controller和/或者异常类型自定义返回的JSON文档:

@ControllerAdvice(basePackageClasses = AcmeController.class)
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(YourException.class)
    @ResponseBody
    ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

}

如果该controller抛出了异常,会使用CustomErrorType POJO的JSON表示而不是ErrorAttributes 表示。

6. Spring HATEOAS

如果正在开发基于超媒体的RESTful API,你可能需要Spring HATEOAS,而Spring Boot会为其提供自动配置。

自动配置取代了@EnableHypermediaSupport,只需注册一定数量的beans就能轻松构建基于超媒体的应用,这些beans包括LinkDiscoverers(客户端支持),ObjectMapper(用于将响应编排为想要的形式)。ObjectMapper可以根据spring.jackson.*属性或Jackson2ObjectMapperBuilder bean进行自定义。

通过注解@EnableHypermediaSupport,你可以控制Spring HATEOAS的配置,但这会禁用上述ObjectMapper的自定义功能。

1. Spring WebFlux 框架

在Spring5.0引入的新的响应式的web框架。

不像Spring MVC,它不要求Servlet API,完全异步、非阻塞,并通过 the Reactor project 实现了 Reactive Streams 规范。

Spring WebFlux有两种风格:

  • 函数式
  • 注解式

注解式风格很像Spring MVC模式:

@RestController
@RequestMapping("/users")
public class MyRestController {

    @GetMapping("/{user}")
    public Mono<User> getUser(@PathVariable Long user) {
        // ...
    }

    @GetMapping("/{user}/customers")
    public Flux<Customer> getUserCustomers(@PathVariable Long user) {
        // ...
    }

    @DeleteMapping("/{user}")
    public Mono<User> deleteUser(@PathVariable Long user) {
        // ...
    }

}

函数式风格:

@Configuration
public class RoutingConfiguration {

    @Bean
    public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
        return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
                .andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
                .andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
    }

}

@Component
public class UserHandler {

    public Mono<ServerResponse> getUser(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> deleteUser(ServerRequest request) {
        // ...
    }
}

详情参考reference documentation

添加 spring-boot-starter-webflux ,去除 spring-boot-starter-web