diff --git a/lab-27/lab-27-webflux-02/pom.xml b/lab-27/lab-27-webflux-02/pom.xml new file mode 100644 index 00000000..2e83aa64 --- /dev/null +++ b/lab-27/lab-27-webflux-02/pom.xml @@ -0,0 +1,32 @@ + + + + org.springframework.boot + spring-boot-starter-parent + 2.2.1.RELEASE + + + 4.0.0 + + lab-27-webflux-02 + + + + + org.springframework.boot + spring-boot-starter-webflux + 2.2.1.RELEASE + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + diff --git a/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java new file mode 100644 index 00000000..83e4bb31 --- /dev/null +++ b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java @@ -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); + } + +} diff --git a/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java new file mode 100644 index 00000000..9183d16d --- /dev/null +++ b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java @@ -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 list() { + // 查询列表 + List 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 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> 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 get4(@RequestParam("id") Integer id) { + // 查询用户 + UserVO user = new UserVO().setId(id).setUsername("username:" + id); + // 返回 + return CommonResult.success(user); + } + +} diff --git a/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserRouter.java b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserRouter.java new file mode 100644 index 00000000..21fc4c77 --- /dev/null +++ b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserRouter.java @@ -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 userListRouterFunction() { + return RouterFunctions.route(RequestPredicates.GET("/users2/list"), + new HandlerFunction() { + + @Override + public Mono handle(ServerRequest request) { + // 查询列表 + List 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 userGetRouterFunction() { + return RouterFunctions.route(RequestPredicates.GET("/users2/get"), + new HandlerFunction() { + + @Override + public Mono 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 demoRouterFunction() { + return route(GET("/users2/demo"), request -> ok().bodyValue("demo")); + } + +} diff --git a/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/package-info.java b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/package-info.java new file mode 100644 index 00000000..2194aca0 --- /dev/null +++ b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 提供核心封装 + */ +package cn.iocoder.springboot.lab27.springwebflux.core; diff --git a/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/vo/CommonResult.java b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/vo/CommonResult.java new file mode 100644 index 00000000..e1929aa5 --- /dev/null +++ b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/vo/CommonResult.java @@ -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 结果泛型 + */ +public class CommonResult 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 返回的泛型 + * @return 新的 CommonResult 对象 + */ + public static CommonResult error(CommonResult result) { + return error(result.getCode(), result.getMessage()); + } + + public static CommonResult error(Integer code, String message) { + Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!"); + CommonResult result = new CommonResult<>(); + result.code = code; + result.message = message; + return result; + } + + public static CommonResult success(T data) { + CommonResult 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 + + '}'; + } + +} diff --git a/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalExceptionHandler.java b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalExceptionHandler.java new file mode 100644 index 00000000..4b5f34e3 --- /dev/null +++ b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalExceptionHandler.java @@ -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()); +// } + +} diff --git a/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalResponseBodyHandler.java b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalResponseBodyHandler.java new file mode 100644 index 00000000..635812c9 --- /dev/null +++ b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalResponseBodyHandler.java @@ -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> writers, RequestedContentTypeResolver resolver) { + super(writers, resolver); + } + + public GlobalResponseBodyHandler(List> writers, RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) { + super(writers, resolver, registry); + } + + @Override + @SuppressWarnings("unchecked") + public Mono handleResult(ServerWebExchange exchange, HandlerResult result) { + Object returnValue = result.getReturnValue(); + Object body = null; + if (returnValue instanceof Mono) { + body = ((Mono) result.getReturnValue()) + .map((Function) GlobalResponseBodyHandler::wrapCommonResult) + .defaultIfEmpty(COMMON_RESULT_SUCCESS); + } else if (returnValue instanceof Flux) { + body = ((Flux) result.getReturnValue()) + .collectList() + .map((Function) GlobalResponseBodyHandler::wrapCommonResult) + .defaultIfEmpty(COMMON_RESULT_SUCCESS); + } else { + body = wrapCommonResult(returnValue); + } + return writeBody(body, METHOD_PARAMETER_MONO_COMMON_RESULT, exchange); + } + + private static Mono methodForParams() { + return null; + } + + private static CommonResult wrapCommonResult(Object body) { + // 如果已经是 CommonResult 类型,则直接返回 + if (body instanceof CommonResult) { + return (CommonResult) body; + } + // 如果不是,则包装成 CommonResult 类型 + return CommonResult.success(body); + } + +} diff --git a/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java new file mode 100644 index 00000000..7dd64075 --- /dev/null +++ b/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java @@ -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; + } + +} diff --git a/lab-27/lab-27-webflux-02/target/classes/META-INF/lab-27-webflux-01.kotlin_module b/lab-27/lab-27-webflux-02/target/classes/META-INF/lab-27-webflux-01.kotlin_module new file mode 100644 index 00000000..2983af70 Binary files /dev/null and b/lab-27/lab-27-webflux-02/target/classes/META-INF/lab-27-webflux-01.kotlin_module differ diff --git a/lab-27/lab-27-webflux-02/target/test-classes/META-INF/lab-27-webflux-01.kotlin_module b/lab-27/lab-27-webflux-02/target/test-classes/META-INF/lab-27-webflux-01.kotlin_module new file mode 100644 index 00000000..2983af70 Binary files /dev/null and b/lab-27/lab-27-webflux-02/target/test-classes/META-INF/lab-27-webflux-01.kotlin_module differ diff --git a/lab-27/pom.xml b/lab-27/pom.xml index 198be1ed..62f16906 100644 --- a/lab-27/pom.xml +++ b/lab-27/pom.xml @@ -13,6 +13,7 @@ pom lab-27-webflux-01 + lab-27-webflux-02