webFlux 学习(三)
不忘初心,方得始终
正文
入门crud
这次我们来学习webflux入门CRUD
首先我们需要安装一个mondodb 在这里我就不说怎么安装了.
配置文件如下
spring.data.mongodb.uri=mongodb://xxxxx:27017/webflux
领域对象
/** * @Created by xiaodao */ @Document(collection = "user") @Data public class User { @Id private String id; @NotBlank private String name; @Range(min = 10,max = 100) private Integer age; }
repository
/** * @Created by xiaodao */ @Repository public interface UserRepository extends ReactiveMongoRepository<User,String> { Flux<User> findByAgeBetween(int start, int end ); @Query("{'age':{$gte:?0,$lte:?1}}") Flux<User> oldUser(int start, int end ); }
Controller
package com.xiaodao.webflux.controller; import com.xiaodao.webflux.pojo.User; import com.xiaodao.webflux.repository.UserRepository; import com.xiaodao.webflux.utils.CheckUtil; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import javax.validation.Valid; import java.awt.*; /** * @Created by xiaodao */ @RestController @RequestMapping("/user") public class UserController { private final UserRepository userRepository; public UserController(UserRepository userRepository) { this.userRepository = userRepository; } /** * 以数组形式一次性返回数据 * @return */ @GetMapping("/") public Flux<User> getAll(){ return userRepository.findAll(); } /** * sse 形式多次返回方式 * @return */ @GetMapping(value = "/stream/all",produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<User> getStreamAll(){ return userRepository.findAll(); } /** * 新增 * @param user * @return */ @PostMapping("/") public Mono<User> createUser(@Valid @RequestBody User user){ CheckUtil.checkName(user.getName()); return this.userRepository.save(user); } /** * 返回状态码 存在返回200 不存在返回404 * @param id * @return */ @DeleteMapping("/{id}") public Mono<ResponseEntity<Void>> delete(@PathVariable String id){ //没有返回值.不知道数据有没有 // this.userRepository.deleteById(id); return this.userRepository.findById(id) //当需要操作数据,并返回一个mono这时候用flatMap //当不操作数据,不返回数据,只是转换数据使用map .flatMap(user->this.userRepository.delete(user) .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } /** * 修改 * 存在的时候返回200 和修改后的数据,不存在的时候返回404 * @param id * @param user * @return */ @PutMapping("/{id}") public Mono<ResponseEntity<User>> updateUser( @PathVariable String id ,@RequestBody @Valid User user){ return this.userRepository.findById(id).flatMap(u->{ user.setId(id); return this.userRepository.save(user); }).map(u->new ResponseEntity<User>(u,HttpStatus.OK)) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } /** * 根据id查找一个值 * @param id * @return */ @GetMapping("/{id}") public Mono<ResponseEntity<User>> findById(@PathVariable String id ){ return this.userRepository.findById(id).map(u->new ResponseEntity<User>(u,HttpStatus.OK)) .defaultIfEmpty(new ResponseEntity<User>(HttpStatus.NOT_FOUND)); } /** * 根据年龄段查找 * @param start * @param end * @return */ @GetMapping("/age/{start}/{end}") public Flux<User> findByage(@PathVariable int start , @PathVariable int end){ return this.userRepository.findByAgeBetween( start,end ); } /** * 流式 * @param start * @param end * @return */ @GetMapping(value = "/ageStream/{start}/{end}",produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<User> findByageStream(@PathVariable int start , @PathVariable int end){ return this.userRepository.findByAgeBetween( start,end ); } @GetMapping("/findoldUser/{start}/{end}") public Flux<User> findoldUser(@PathVariable int start , @PathVariable int end){ return this.userRepository.oldUser( start,end ); } }
上面的代码加了一点hibernate的校验和自己的校验
controllerAdvice
/** * 异常处理 * @Created by xiaodao */ @ControllerAdvice public class CheckAdvice { @ExceptionHandler(WebExchangeBindException.class) public ResponseEntity<String> handleBindException(WebExchangeBindException e ){ return new ResponseEntity<String>(tostr(e), HttpStatus.BAD_REQUEST); } /** * 校验异常转换为字符串 * @param e * @return */ private String tostr(WebExchangeBindException e) { return e.getFieldErrors().stream() .map(a->a.getField()+" : "+ a.getDefaultMessage()) .reduce("",(s1,s2)-> s1+ "\n"+s2); } @ExceptionHandler(CheckException.class) public ResponseEntity<String> handleCheckException( CheckException e) { return new ResponseEntity<String>(toStr(e), HttpStatus.BAD_REQUEST); } private String toStr(CheckException e) { return e.getFieldName() + ": 错误的值 " + e.getFieldValue(); } }
这里注意的一点就是@valid需要加载具体的类上,不然不会生效
这里还有我们的自己定义的校验名字的方法,我也贴到这里
/** * @Created by xiaodao */ public class CheckUtil { private static final String[] INVALID_NAMES={"admin","role"}; public static void checkName(String value){ Stream.of(INVALID_NAMES).filter(name -> name.equalsIgnoreCase(value)) .findAny().ifPresent(name->{throw new CheckException("name",value);}); } }
RouterFucntion
routerFunction 是webflux的另一种开始模式
webflux 可以运行在servlet3.1 和netty上 需要把
httpServletRequest - >serverRequest
httpServletResponse - >serverResponse
使用routerFunction开发过程需要
1.HandlerFunction 就是输入servletRequest 返回的是 serverResponse
2.routerFunction (将请求url和HandleFunction对应起来)
3.然后才是你自己定义的userhandle处理
4.server 处理
代码如下
package com.xiaodao.webflux.handle; import com.xiaodao.webflux.pojo.User; import com.xiaodao.webflux.repository.UserRepository; import com.xiaodao.webflux.utils.CheckUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Mono; /** * @Created by xiaodao */ @Component public class UserHandle { @Autowired private UserRepository userRepository; /*** * 获取所有用户 * @param request * @return */ public Mono<ServerResponse> getAllUser(ServerRequest request){ return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8) .body(userRepository.findAll(), User.class); } /** * 创建用户 * @param request * @return */ public Mono<ServerResponse> createUser(ServerRequest request) { Mono<User> userMono = request.bodyToMono(User.class); return userMono.flatMap(u -> { CheckUtil.checkName(u.getName()); return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8) .body(userRepository.save(u), User.class); } ); } /** * 删除 * @param request * @return */ public Mono<ServerResponse> deleteUser(ServerRequest request){ String id = request.pathVariable("id"); return userRepository.findById(id).flatMap(user->userRepository.delete(user)) .then(ServerResponse.ok().build()) .switchIfEmpty(ServerResponse.notFound().build()); } }
routerFunction
/** * @Created by xiaodao */ @Configuration public class AllRouter { @Bean RouterFunction<ServerResponse> userRouter(UserHandle userHandle){ return RouterFunctions.nest( RequestPredicates.path("/user"), //查找所有用户 RouterFunctions.route(RequestPredicates.GET("/"),userHandle::getAllUser) //创建用户 .andRoute(RequestPredicates.POST("/") .and(RequestPredicates.accept(MediaType.APPLICATION_JSON_UTF8) ) ,userHandle::createUser) //删除用户 .andRoute(RequestPredicates.DELETE("/{id}"),userHandle::deleteUser) ); } }
/** * @Created by xiaodao */ @Component @Order(-2) public class ExceptionHandle implements WebExceptionHandler { @Override public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) { ServerHttpResponse response = exchange.getResponse(); //设置响应头400 response.setStatusCode(HttpStatus.BAD_REQUEST); //设置返回类型 response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); //异常信息 String errorMsg = tostr(throwable); DataBuffer wrap = response.bufferFactory().wrap(errorMsg.getBytes()); return response.writeWith(Mono.just(wrap)); } private String tostr(Throwable ex) { //已知异常 if(ex instanceof CheckException){ CheckException check = (CheckException)ex; return check.getFieldName() +" invalid value "+ check.getFieldValue(); //未知异常需要打印堆栈方便定位 }else{ ex.printStackTrace(); return ex.toString(); } } }
这个时候我来就可以去访问.
localhost:8080/user
一起交流进步.扫描下方QQ二维码即可

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义