入門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