“This is the 31st day of my participation in the First Challenge 2022. For details: First Challenge 2022”

When using the functional programming model, we need to initialize the server ourselves.

In the function-based programming model, there are two core interfaces, RouterFunction and HandlerFunction,

RouterFunction implements the routing function to forward requests to the corresponding handler.

The HandlerFunction represents the function that handles the incoming request and generates the response.

In this programming model, our core task is to define the implementation of these two functional interfaces and start the required servers.

In Spring MVC, the two objects that represent server HTTP requests and responses are ServletRequest and ServletRespose, but in Spring WebFlux, The two objects that represent server-side HTTP requests and responses are ServerRequest and ServerRespose.

1. Spring WebFlux is implemented based on functional programming model

1. Create a Springboot project and add related dependencies

    <! - webflux dependence - >
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
        <version>2.6.3</version>
    </dependency>

    <! --lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
    </dependency>
Copy the code

2. Write a configuration file

server.port=8081
Copy the code

3, write file directory structure

Entity class: User

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private int age;
}
Copy the code

Service layer interface: UserService

public interface UserService {

    // Return one or zero using Mono by id query
    Mono<User> queryById(int id);

    // To query all, return multiple uses of Flux
    Flux<User> query(a);

    // Add a user
    Mono<Void> insert(Mono<User> user);
}
Copy the code

4. Create Handler (how to do this)

When importing packages, import packages under Reactive

package com.haoming.handler;

import com.haoming.entity.User;
import com.haoming.service.UserService;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.servlet.ServletException;
import java.io.IOException;

import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;

public class UserHandler {

    private final UserService userService;

    public UserHandler(UserService userService){
        this.userService = userService;
    }

    // Query by id
    public Mono<ServerResponse> queryById(ServerRequest serverRequest){
        Get the ID from the request
        int id = Integer.valueOf(serverRequest.pathVariable("id"));// Null value handling,notFound() : creates a 404 Not Found state builder, build() : builds a response entity with no content
        Mono<ServerResponse> build = ServerResponse.notFound().build();

        Mono<User> userMono = this.userService.queryById(id);

        /** Use the operator to convert userMono to Mono
      
       , * flatMap: flatMap returns a new Mono, * OK () : create a builder with state set to 200 OK * contentType: Sets the body type of the body: sets the body of the response to the given BodyInserter and returns switchIfEmpty: If flatMap returns null, the null value processing is called */
      
        return userMono.flatMap(person -> ServerResponse
                .ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(fromValue(person))
                .switchIfEmpty(build));
    }

    // Query all
    public Mono<ServerResponse> query(a){
        Flux<User> users = this.userService.query();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
    }

    / / add
    public Mono<ServerResponse> insert(ServerRequest serverRequest) throws ServletException, IOException {
        // Get the user object
        Mono<User> userMono = serverRequest.bodyToMono(User.class);
        return ServerResponse.ok().build(this.userService.insert(userMono)); }}Copy the code

5. Initialize the server and write the Router (implement the routing function)

package com.haoming;


import com.haoming.handler.UserHandler;
import com.haoming.service.UserService;
import com.haoming.service.impl.UserServiceImpl;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.netty.http.server.HttpServer;

import java.io.IOException;

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;

public class Server {

    // Final call
    public static void main(String[] args) throws IOException {
        Server server = new Server();
        server.createReactorServer();
        System.out.println("enter to exit");
        // Reads the next byte from the input stream
        System.in.read();
    }
    // Create a route
    public RouterFunction<ServerResponse> routerFunction(a){
        UserService userService = new UserServiceImpl();
        UserHandler userHandler = new UserHandler(userService);

        // With /users/{id} you can bind to a specific method in Handler and specify that it should be returned in JSON format
       return RouterFunctions.route(
                GET("/users/{id}").and(accept(APPLICATION_JSON)),userHandler::queryById)
               .andRoute(GET("/query").and(accept(APPLICATION_JSON)),userHandler::query);
    }
    // Create a server
    public void createReactorServer(a){
        // Routing ADAPTS to the handler handler
        RouterFunction<ServerResponse> route = routerFunction();
        /** * route: the routerFunction to convert * returns: the HTTP handler that handles the HTTP request */ using the given routerFunction routerFunction()
        HttpHandler httpHandler = toHttpHandler(route);
        / / adapter
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

        // Create an HTTP server,HttpServer: represents a server instance
        HttpServer httpServer = HttpServer.create();
        //bindNow() : Starts the server in blocking mode and waits for it to complete initializationhttpServer.handle(adapter).bindNow(); }}Copy the code

Start the server for testing:

After the server is successfully started, you can see that the port number of the server is 55953

Test according to the query: http://localhost:55953/users/1

All test query: http://localhost:55953/query

Test successful!

2. Use WebClient to call

The reactive, non-blocking Web request client provided by WebClient is the counterpart of an older restTemplate class, which does not block code and executes asynchronously.

Write the WebClient:

package com.haoming;

import com.haoming.entity.User;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

public class Client {
    public static void main(String[] args) {
        // Call server address
        WebClient webClient = WebClient.create("http://127.0.0.1:55953");

        // Query by id
        String id = "1";
        /** * get().uri("/users/{id}", Id): specifies the URI of the request * Retrieve (): executes the HTTP request and receives the body in JSON format * bodyToMono(user.class): specifies that the request result needs to be processed as User and wrapped as Reactor's Mono object * Block (): blocks the result of getting the response */
        User user = webClient.get().uri("/users/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).block();
        // Prints the user name
        System.out.println(user.getName());

        // Query all
        Flux<User> user1 = webClient.get().uri("/query", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);

        // Prints user informationuser1.map(user2 -> user2).buffer().doOnNext(System.out::println).blockFirst(); }}Copy the code

Start the WebClient test: The query result is successfully printed