SpringBoot3 Web开发新特性(Problemdetails、函数式Web)

发布时间:2023年12月18日

1. Problemdetails

错误信息返回新格式

package org.springframework.boot.autoconfigure.web.servlet;
......省略部分......
public class WebMvcAutoConfiguration {
    ......省略部分......
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnProperty(
        prefix = "spring.mvc.problemdetails",
        name = {"enabled"},
        havingValue = "true"
    )
    static class ProblemDetailsErrorHandlingConfiguration {
        ProblemDetailsErrorHandlingConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean({ResponseEntityExceptionHandler.class})
        ProblemDetailsExceptionHandler problemDetailsExceptionHandler() {
            return new ProblemDetailsExceptionHandler();
        }
    }
    ......省略部分......
}
  1. 默认是关闭的。需要配置一个属性spring.mvc.problemdetails.enabled=true
  2. ProblemDetailsExceptionHandler是一个@ControllerAdvice集中处理系统异常
  3. 如果系统出现以下异常,会被SpringBoot支持以RFC 7807规范方式返回错误数据
	@ExceptionHandler({
			HttpRequestMethodNotSupportedException.class,    // 请求方式不支持
			HttpMediaTypeNotSupportedException.class,
			HttpMediaTypeNotAcceptableException.class,
			MissingPathVariableException.class,
			MissingServletRequestParameterException.class,
			MissingServletRequestPartException.class,
			ServletRequestBindingException.class,
			MethodArgumentNotValidException.class,
			NoHandlerFoundException.class,
			AsyncRequestTimeoutException.class,
			ErrorResponseException.class,
			ConversionNotSupportedException.class,
			TypeMismatchException.class,
			HttpMessageNotReadableException.class,
			HttpMessageNotWritableException.class,
			BindException.class
		})
  1. 以HttpRequestMethodNotSupportedException来讲解开启的效果。不开启前,以post方式请求一个get url: http://localhost:8080/exception。显示如下。其中headers的Content-Type是application/json
{
    "timestamp": "2023-07-22T02:51:16.088+00:00",
    "status": 405,
    "error": "Method Not Allowed",
    "path": "/exception"
}

开启后。显示如下。。其中headers的Content-Type是application/problem+json。其中problem可以通过额外的拓展返回一些业务数据

{
    "type": "about:blank",
    "title": "Method Not Allowed",
    "status": 405,
    "detail": "Method 'POST' is not supported.",
    "instance": "/exception"
}

2. 函数式Web

使用函数式的方式,定义Web的请求处理流程。使用的java8函数式接口

Web请求处理的方式:

  1. @Controller + @RequestMapping:耦合式 (路由、业务耦合)
  2. 函数式Web:分离式(路由、业务分离)

2.1 场景

以Restful方式,对User进行CRUD

  • GET /user/1: 获取id=1的用户
  • GET /users: 获取所有用户
  • POST /user: 请求体携带json,新增一个用户
  • PUT /user/1: 请求体携带json,修改id=1的用户
  • DELETE /user/1: 删除id=1的用户

2.2 主要逻辑

  1. 给容器中放一个Bean:类型是RouterFunction,集中所有路由信息
  2. 每个业务准备一个的Handler

2.3 核心对象

  1. RouterFunction:定义路由信息。发什么请求,谁来处理
  2. RequestPredicate:定义请求规则:请求方式(GET、POST)和请求参数
  3. ServerRequest:封装请求完整数据
  4. ServerResponse:封装响应完整数据

2.4 示例程序

WebFunctionConfig.java

package com.hh.springboot3test.config;

import com.hh.springboot3test.bean.User;
import com.hh.springboot3test.biz.UserBizHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.RequestPredicates;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.RouterFunctions;
import org.springframework.web.servlet.function.ServerResponse;


@Configuration
public class WebFunctionConfig {

    @Bean
    public RouterFunction<ServerResponse> userRoute(UserBizHandler userBizHandler/*这个会被自动注入进来*/) {

        return RouterFunctions.route() // 开始定义路由信息
                .GET("/user/{id}", RequestPredicates.accept(MediaType.ALL).and(RequestPredicates.param("key1", "value1")/* 只有/user/n?key1=value1能查询出来结果 */), request -> {
                    String id = request.pathVariable("id");
                    System.out.println("查询的用户的id = " + id);
                    // 模拟数据库查询出来的用户
                    User user = new User(1, "jim", 28);
                    // 构造响应。和@ResponseBody原理一样,利用HttpMessageConverter,可写出为json
                    return ServerResponse.ok().body(user);
                })
                .GET("/users", userBizHandler::getUsers)
                .POST("/user", RequestPredicates.accept(MediaType.APPLICATION_JSON), userBizHandler::saveUser)
                .PUT("/user/{id}", RequestPredicates.accept(MediaType.APPLICATION_JSON), userBizHandler::updateUser)
                .DELETE("/user/{id}", userBizHandler::deleteUser)
                .build();
    }

}

UserBizHandler.java

package com.hh.springboot3test.biz;

import com.hh.springboot3test.bean.User;
import jakarta.servlet.ServletException;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;


@Service
public class UserBizHandler {

    /**
     * 获取所有用户
     */
    public ServerResponse getUsers(ServerRequest request) throws Exception {
        // 模拟数据库查询出来的所有用户
        List<User> users = Arrays.asList(
                new User(1, "jim", 28),
                new User(2, "lily", 18)
        );

        return ServerResponse
                .ok()
                .body(users);
    }


    /**
     * 保存用户
     */
    public ServerResponse saveUser(ServerRequest request) throws ServletException, IOException {
        // 提取请求体
        User user = request.body(User.class);

        // 模拟保存用户
        System.out.println("保存的用户是: " + user);

        return ServerResponse.ok().build();
    }

    /**
     * 更新用户
     */
    public ServerResponse updateUser(ServerRequest request) throws ServletException, IOException {
        // 提取请求体
        User user = request.body(User.class);

        // 模拟更新用户
        System.out.println("更新的用户是: " + user);

        return ServerResponse.ok().build();
    }

    /**
     * 删除用户
     */
    public ServerResponse deleteUser(ServerRequest request) {
        String id = request.pathVariable("id");

        // 模拟删除用户
        System.out.println("删除的用户的id = " + id);

        return ServerResponse.ok().build();
    }
}
文章来源:https://blog.csdn.net/yy8623977/article/details/131864850
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。