在之前的博客,我向大家简单展示了 WebFlux 框架的简单使用,算是有了一个基本的认识,上一次的案例我们使用的是基于注解开发的模式,而其实 WebFlux 还有另外一种写法,基于功能性的模式。其实两种方式都比较接近,下面我们通过编写代码来感受一下。
首先,创建一个简单的 spring boot 项目,导入 WebFlux 依赖以及 为了方便导入 Lombok 依赖,如下:
org.springframework.boot spring-boot-starter-webfluxorg.projectlombok lombok
回想一下,我们在注解开发模式下,接下来就是要编写 controller 代码,而在功能性模式下,我们编写的是 handler 代码,为了方便,我们把 handler 类注入到容器中,交由 Spring 去管理,如下:
@Component public class UserHandler { private final UserService userService; public UserHandler(UserService userService) { this.userService = userService; } // 根据id查询用户信息 public MonogetUserById(ServerRequest request) { // 获取路径中的 id 值 Integer id = Integer.valueOf(request.pathVariable("id")); return ServerResponse .ok() .contentType(MediaType.APPLICATION_JSON) .body(userService.getUserById(id), User.class); } // 获取所有用户信息 public Mono getUsers(ServerRequest request) { Flux users = userService.getUsers(); return ServerResponse .ok() .contentType(MediaType.APPLICATION_JSON) .body(users, User.class); } // 新增用户信息 public Mono saveUser(ServerRequest request) { Mono userMono = request.bodyToMono(User.class); return userMono.flatMap(user -> { // 校验名称 CheckUtils.checkName(user.getUsername()); return ServerResponse.ok() .contentType(MediaType.APPLICATION_JSON) .body(userService.saveUser(userMono), User.class); }); } }
注意代码中 CheckUtils.checkName(user.getUsername()); 这一句是我编写的校验代码
接下来就是编写对应的路径映射了,我们直接编写一个配置类,向容器中注入一个 RouterFunction 即可,代码如下:
import static org.springframework.web.reactive.function.server.RequestPredicates.*; @Configuration public class WebFluxConfig { @Bean public RouterFunctionrouterFunction(UserHandler userHandler) { return RouterFunctions.route( GET("/user/{id}") .and(accept(MediaType.APPLICATION_JSON)) , userHandler::getUserById // 调用 handler 中的方法 ).andRoute( GET("/user") .and(accept(MediaType.APPLICATION_JSON)) , userHandler::getUsers ).andRoute( POST("/saveuser") , userHandler::saveUser ); } }
最后与注解开发案例类似,编写 service 服务类代码,如下:
public interface UserService { public MonogetUserById(Integer id); public Flux getUsers(); public Mono saveUser(Mono userMono); }
@Service public class UserServiceImpl implements UserService { private final static MapUSER_MAP = new HashMap<>(); public UserServiceImpl() { USER_MAP.put(1, new User("lisi", "12345", "12121@qq.com", 20)); USER_MAP.put(2, new User("lisi2", "12345", "12121@qq.com", 20)); USER_MAP.put(3, new User("lisi3", "12345", "12121@qq.com", 20)); USER_MAP.put(4, new User("lisi4", "12345", "12121@qq.com", 20)); USER_MAP.put(5, new User("lisi5", "12345", "12121@qq.com", 20)); } @Override public Mono getUserById(Integer id) { return Mono.justOrEmpty(USER_MAP.get(id)); } @Override public Flux getUsers() { return Flux.fromIterable(USER_MAP.values()); } @Override public Mono saveUser(Mono userMono) { return userMono.doOnNext(user -> { int id = USER_MAP.size() + 1; USER_MAP.put(id, user); }).thenEmpty(Mono.empty()); } }
到这里,其实我们的代码已经编写完成了,回到前面的代码,我编写了一个校验的代码,我们来完成一下,首先准备一个用于校验的自定义异常类和工具类:
@Data public class CheckException extends RuntimeException { private static final long serialVersionUID = 3899025507910942091L; private String filedName; private String filedValue; public CheckException() { super(); } public CheckException(String message) { super(message); } public CheckException(String message, Throwable cause) { super(message, cause); } public CheckException(Throwable cause) { super(cause); } public CheckException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } public CheckException(String filedName, String filedValue) { this.filedName = filedName; this.filedValue = filedValue; } }
public class CheckUtils { private static final String[] INVALID_NAME = {"admin","guanliyuan"}; public static void checkName(String value) { Stream.of(INVALID_NAME) .filter(name -> name.equalsIgnoreCase(value)) .findAny() .ifPresent(name -> { // 如果 value 与 INVALID_NAME 的值相等,抛出自定义的异常 throw new CheckException("name", value); }); } }
最后,我们需要编写一个用于处理异常的 handler 去实现 ErrorWebExceptionHandler 接口,如下:
@Component @Order(-2) // 调整优先级 public class ExceptionHandler implements ErrorWebExceptionHandler { @Override public Monohandle(ServerWebExchange exchange, Throwable ex) { ServerHttpResponse response = exchange.getResponse(); // 设置响应编码 400 response.setStatusCode(HttpStatus.BAD_REQUEST); // 设置返回类型 response.getHeaders().setContentType(MediaType.TEXT_PLAIN); // 得到异常信息 String exMessage = toStr(ex); DataBuffer wrap = response.bufferFactory().wrap(exMessage.getBytes()); return response.writeWith(Mono.just(wrap)); } private String toStr(Throwable ex) { // 已知异常 if(ex instanceof CheckException) { CheckException exception = (CheckException) ex; return exception.getFiledName() + ": invalid value " + exception.getFiledValue(); } // 未知异常 else { ex.printStackTrace(); return ex.toString(); } } }
这里需要注意的是一定要加上 @Order(-2),因为默认的错误处理,在 Spring Boot 官方提供了一个 ErrorExceptionHandler,它的等级为 -1,还有需要注意的是实现的类是 ErrorExceptionHandler而不是 WebExceptionHandler,这里一定要注意,否则会出现无论等级设置为多少都不会响应自定义的 handler 的情况。从下面的代码我们也可以看到,默认的 ErrorExceptionHandler 上面有一个 @ConditionalOnMissingBean 也就是说对我们没有这个 bean 在容器中时下面的才会生效,否则使用的是我们自定义的。
这样,我们的代码才算是真正的完成了,然后就是进行测试了,我使用的是 postman 工具进行测试。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)