Spring Reactive Mongo Router Handler 中的 UpdateById 方法

UpdateById method in Spring Reactive Mongo Router Handler

在 Spring Reactive Java 中,我如何使用 Router 和 Handler 编写 updateById() 方法?

例如路由器有这样的代码:

RouterFunctions.route(RequestPredicates.PUT("/employees/{id}").and(RequestPredicates.accept(MediaType.APPLICATION_JSON))
    .and(RequestPredicates.contentType(MediaType.APPLICATION_JSON)),
        employeeHandler::updateEmployeeById);

我的问题是如何编写 employeeHandler::updateEmployeeById() 保持 ID 相同但更改 Employee 对象的其他成员?

public Mono<ServerResponse> updateEmployeeById(ServerRequest serverRequest) {
    Mono<Employee> employeeMono = serverRequest.bodyToMono(Employee.class);

    <And now what??>

    return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(employeeMono, Employee.class);
}

员工 class 看起来像这样:

@Document
@Data  
@AllArgsConstructor  
@NoArgsConstructor  
public class Employee {

    @Id
    int id;
    double salary;

}

感谢您的帮助。

首先,你必须在类路径中添加ReactiveMongoRepository。您也可以阅读 here.

    @Repository
    public interface EmployeeRepository extends ReactiveMongoRepository<Employee, Integer> {
        Mono<Employee> findById(Integer id);
    }

那么你的updateEmployeeById方法可以有如下结构:

    public Mono<ServerResponse> updateEmployeeById(ServerRequest serverRequest) {
    return serverRequest
            .bodyToMono(Employee.class)
            .doOnSubscribe(e -> log.info("update employee request received"))
            .flatMap(employee -> {
                Integer id = Integer.parseInt(serverRequest.pathVariable("id"));
                return employeeRepository
                        .findById(id)
                        .switchIfEmpty(Mono.error(new NotFoundException("employee with " + id + " has not been found")))

                        // what you need to do is to update already found entity with
                        // new values. Usually map() function is used for that purpose
                        // because map is about 'transformation' what is setting new
                        // values in our case
                        .map(foundEmployee -> {
                            foundEmployee.setSalary(employee.getSalary());
                            return foundEmployee;
                        });
            })
            .flatMap(employeeRepository::save)
            .doOnError(error -> log.error("error while updating employee", error))
            .doOnSuccess(e -> log.info("employee [{}] has been updated", e.getId()))
            .flatMap(employee -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(employee), Employee.class));
}

更新:

根据 Prana 的回答,我更新了上面的代码,将我们的解决方案合二为一。添加了借助 Slf4j 进行日志记录。并且 switchIfEmpty() 适用于未找到实体的情况。

我还建议您阅读 global exception handling,这将使您的 API 变得更好。我可以在这里提供其中的一部分:

/**
 * Returns routing function.
 *
 * @param errorAttributes errorAttributes
 * @return routing function
 */
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
    return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}

private HttpStatus getStatus(Throwable error) {
    HttpStatus status;
    if (error instanceof NotFoundException) {
        status = NOT_FOUND;
    } else if (error instanceof ValidationException) {
        status = BAD_REQUEST;
    } else {
        status = INTERNAL_SERVER_ERROR;
    }
    return status;
}

/**
 * Custom global error handler.
 *
 * @param request request
 * @return response
 */
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {

    Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);

    Throwable error = getError(request);
    HttpStatus errorStatus = getStatus(error);

    return ServerResponse
            .status(errorStatus)
            .contentType(APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorPropertiesMap));
}

上面的版本略有不同,没有任何例外:

public Mono<ServerResponse> updateEmployeeById(ServerRequest serverRequest) {

        Mono<ServerResponse> notFound = ServerResponse.notFound().build();

        Mono<Employee> employeeMono = serverRequest.bodyToMono(Employee.class);
        Integer employeeId = Integer.parseInt(serverRequest.pathVariable("id"));

        employeeMono = employeeMono.flatMap(employee -> employeeRepository.findById(employeeId)
                .map(foundEmployee -> {                 
                    foundEmployee.setSalary(employee.getSalary());
                    return foundEmployee;
                })
                .flatMap(employeeRepository::save));

        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(employeeMono, Employee.class).switchIfEmpty(notFound);
    }

感谢 Stepan Tsybulski。