spring webflux 实现全局统一返回

This commit is contained in:
YunaiV
2019-11-25 01:52:32 +08:00
parent fe88a20315
commit a2824323b1
12 changed files with 491 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lab-27-webflux-02</artifactId>
<dependencies>
<!-- 实现对 Spring WebFlux 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!-- 方便等会写单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,29 @@
package cn.iocoder.springboot.lab27.springwebflux;
import cn.iocoder.springboot.lab27.springwebflux.core.web.GlobalResponseBodyHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
@SpringBootApplication
public class Application {
@Autowired
ServerCodecConfigurer serverCodecConfigurer;
@Autowired
RequestedContentTypeResolver requestedContentTypeResolver;
@Bean
public GlobalResponseBodyHandler responseWrapper() {
return new GlobalResponseBodyHandler(serverCodecConfigurer
.getWriters(), requestedContentTypeResolver);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,94 @@
package cn.iocoder.springboot.lab27.springwebflux.controller;
import cn.iocoder.springboot.lab27.springwebflux.core.vo.CommonResult;
import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
/**
* 用户 Controller
*/
@RestController
@RequestMapping("/users")
public class UserController {
/**
* 查询用户列表
*
* @return 用户列表
*/
@GetMapping("/list")
public Flux<UserVO> list() {
// 查询列表
List<UserVO> result = new ArrayList<>();
result.add(new UserVO().setId(1).setUsername("yudaoyuanma"));
result.add(new UserVO().setId(2).setUsername("woshiyutou"));
result.add(new UserVO().setId(3).setUsername("chifanshuijiao"));
// 返回列表
return Flux.fromIterable(result);
}
/**
* 获得指定用户编号的用户
*
* @param id 用户编号
* @return 用户
*/
@GetMapping("/get")
public Mono<UserVO> get(@RequestParam("id") Integer id) {
// 查询用户
UserVO user = new UserVO().setId(id).setUsername("username:" + id);
// 返回
return Mono.just(user);
}
/**
* 获得指定用户编号的用户
*
* @param id 用户编号
* @return 用户
*/
@GetMapping("/get2")
public Mono<CommonResult<UserVO>> get2(@RequestParam("id") Integer id) {
// 查询用户
UserVO user = new UserVO().setId(id).setUsername("username:" + id);
// 返回
return Mono.just(CommonResult.success(user));
}
/**
* 获得指定用户编号的用户
*
* @param id 用户编号
* @return 用户
*/
@GetMapping("/get3")
public UserVO get3(@RequestParam("id") Integer id) {
// 查询用户
UserVO user = new UserVO().setId(id).setUsername("username:" + id);
// 返回
return user;
}
/**
* 获得指定用户编号的用户
*
* @param id 用户编号
* @return 用户
*/
@GetMapping("/get4")
public CommonResult<UserVO> get4(@RequestParam("id") Integer id) {
// 查询用户
UserVO user = new UserVO().setId(id).setUsername("username:" + id);
// 返回
return CommonResult.success(user);
}
}

View File

@@ -0,0 +1,67 @@
package cn.iocoder.springboot.lab27.springwebflux.controller;
import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.server.*;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.*;
import static org.springframework.web.reactive.function.server.ServerResponse.*;
/**
* 用户 Router
*/
@Configuration
public class UserRouter {
@Bean
public RouterFunction<ServerResponse> userListRouterFunction() {
return RouterFunctions.route(RequestPredicates.GET("/users2/list"),
new HandlerFunction<ServerResponse>() {
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
// 查询列表
List<UserVO> result = new ArrayList<>();
result.add(new UserVO().setId(1).setUsername("yudaoyuanma"));
result.add(new UserVO().setId(2).setUsername("woshiyutou"));
result.add(new UserVO().setId(3).setUsername("chifanshuijiao"));
// 返回列表
return ServerResponse.ok().bodyValue(result);
}
});
}
@Bean
public RouterFunction<ServerResponse> userGetRouterFunction() {
return RouterFunctions.route(RequestPredicates.GET("/users2/get"),
new HandlerFunction<ServerResponse>() {
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
// 获得编号
Integer id = request.queryParam("id")
.map(s -> StringUtils.isEmpty(s) ? null : Integer.valueOf(s)).get();
// 查询用户
UserVO user = new UserVO().setId(id).setUsername(UUID.randomUUID().toString());
// 返回列表
return ServerResponse.ok().bodyValue(user);
}
});
}
@Bean
public RouterFunction<ServerResponse> demoRouterFunction() {
return route(GET("/users2/demo"), request -> ok().bodyValue("demo"));
}
}

View File

@@ -0,0 +1,4 @@
/**
* 提供核心封装
*/
package cn.iocoder.springboot.lab27.springwebflux.core;

View File

@@ -0,0 +1,102 @@
package cn.iocoder.springboot.lab27.springwebflux.core.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* 通用返回结果
*
* @param <T> 结果泛型
*/
public class CommonResult<T> implements Serializable {
public static Integer CODE_SUCCESS = 0;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 返回数据
*/
private T data;
/**
* 将传入的 result 对象,转换成另外一个泛型结果的对象
*
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
*
* @param result 传入的 result 对象
* @param <T> 返回的泛型
* @return 新的 CommonResult 对象
*/
public static <T> CommonResult<T> error(CommonResult<?> result) {
return error(result.getCode(), result.getMessage());
}
public static <T> CommonResult<T> error(Integer code, String message) {
Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!");
CommonResult<T> result = new CommonResult<>();
result.code = code;
result.message = message;
return result;
}
public static <T> CommonResult<T> success(T data) {
CommonResult<T> result = new CommonResult<>();
result.code = CODE_SUCCESS;
result.data = data;
result.message = "";
return result;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@JsonIgnore
public boolean isSuccess() {
return CODE_SUCCESS.equals(code);
}
@JsonIgnore
public boolean isError() {
return !isSuccess();
}
@Override
public String toString() {
return "CommonResult{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}

View File

@@ -0,0 +1,48 @@
package cn.iocoder.springboot.lab27.springwebflux.core.web;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ControllerAdvice(basePackages = "cn.iocoder.springboot.lab23.springmvc.controller")
public class GlobalExceptionHandler {
// private Logger logger = LoggerFactory.getLogger(getClass());
//
// /**
// * 处理 ServiceException 异常
// */
// @ResponseBody
// @ExceptionHandler(value = ServiceException.class)
// public CommonResult serviceExceptionHandler(HttpServletRequest req, ServiceException ex) {
// logger.debug("[serviceExceptionHandler]", ex);
// // 包装 CommonResult 结果
// return CommonResult.error(ex.getCode(), ex.getMessage());
// }
//
// /**
// * 处理 MissingServletRequestParameterException 异常
// *
// * SpringMVC 参数不正确
// */
// @ResponseBody
// @ExceptionHandler(value = MissingServletRequestParameterException.class)
// public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) {
// logger.debug("[missingServletRequestParameterExceptionHandler]", ex);
// // 包装 CommonResult 结果
// return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(),
// ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage());
// }
//
// /**
// * 处理其它 Exception 异常
// */
// @ResponseBody
// @ExceptionHandler(value = Exception.class)
// public CommonResult exceptionHandler(HttpServletRequest req, Exception e) {
// // 记录异常日志
// logger.error("[exceptionHandler]", e);
// // 返回 ERROR CommonResult
// return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(),
// ServiceExceptionEnum.SYS_ERROR.getMessage());
// }
}

View File

@@ -0,0 +1,79 @@
package cn.iocoder.springboot.lab27.springwebflux.core.web;
import cn.iocoder.springboot.lab27.springwebflux.core.vo.CommonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.function.Function;
public class GlobalResponseBodyHandler extends ResponseBodyResultHandler {
private static Logger LOGGER = LoggerFactory.getLogger(GlobalResponseBodyHandler.class);
private static MethodParameter METHOD_PARAMETER_MONO_COMMON_RESULT;
private static final CommonResult COMMON_RESULT_SUCCESS = CommonResult.success(null);
static {
try {
// 获得 METHOD_PARAMETER_MONO_COMMON_RESULT 。其中 -1 表示 `#methodForParams()` 方法的返回值
METHOD_PARAMETER_MONO_COMMON_RESULT = new MethodParameter(
GlobalResponseBodyHandler.class.getDeclaredMethod("methodForParams"), -1);
} catch (NoSuchMethodException e) {
LOGGER.error("[static][获取 METHOD_PARAMETER_MONO_COMMON_RESULT 时,找不都方法");
throw new RuntimeException(e);
}
}
public GlobalResponseBodyHandler(List<HttpMessageWriter<?>> writers, RequestedContentTypeResolver resolver) {
super(writers, resolver);
}
public GlobalResponseBodyHandler(List<HttpMessageWriter<?>> writers, RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) {
super(writers, resolver, registry);
}
@Override
@SuppressWarnings("unchecked")
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
Object returnValue = result.getReturnValue();
Object body = null;
if (returnValue instanceof Mono) {
body = ((Mono<Object>) result.getReturnValue())
.map((Function<Object, Object>) GlobalResponseBodyHandler::wrapCommonResult)
.defaultIfEmpty(COMMON_RESULT_SUCCESS);
} else if (returnValue instanceof Flux) {
body = ((Flux<Object>) result.getReturnValue())
.collectList()
.map((Function<Object, Object>) GlobalResponseBodyHandler::wrapCommonResult)
.defaultIfEmpty(COMMON_RESULT_SUCCESS);
} else {
body = wrapCommonResult(returnValue);
}
return writeBody(body, METHOD_PARAMETER_MONO_COMMON_RESULT, exchange);
}
private static Mono<CommonResult> methodForParams() {
return null;
}
private static CommonResult<?> wrapCommonResult(Object body) {
// 如果已经是 CommonResult 类型,则直接返回
if (body instanceof CommonResult) {
return (CommonResult<?>) body;
}
// 如果不是,则包装成 CommonResult 类型
return CommonResult.success(body);
}
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.springboot.lab27.springwebflux.vo;
/**
* 用户 VO
*/
public class UserVO {
/**
* 编号
*/
private Integer id;
/**
* 账号
*/
private String username;
public Integer getId() {
return id;
}
public UserVO setId(Integer id) {
this.id = id;
return this;
}
public String getUsername() {
return username;
}
public UserVO setUsername(String username) {
this.username = username;
return this;
}
}

View File

@@ -13,6 +13,7 @@
<packaging>pom</packaging>
<modules>
<module>lab-27-webflux-01</module>
<module>lab-27-webflux-02</module>
</modules>