Showing some Mono/Flux-based endpoints

Let's start with a simple HTTP GET. Similar to Spring MVC endpoints, Spring WebFlux supports Flux operations as shown here:

    @GetMapping(API_BASE_PATH + "/images") 
    Flux<Image> images() { 
      return Flux.just( 
        new Image("1", "learning-spring-boot-cover.jpg"), 
        new Image("2", "learning-spring-boot-2nd-edition-cover.jpg"), 
        new Image("3", "bazinga.png") 
      ); 
    } 

This preceding controller can be described as follows:

  • Using the same Flux.just() helper, we return a rather contrived list
  • The Spring controller returns a Flux<Image> Reactor type, leaving Spring in charge of properly subscribing to this flow when the time is right

Before we can move forward, we need to define this Image data type like this:

    @Data 
    @NoArgsConstructor 
    public class Image { 
 
      private String id; 
      private String name; 
 
      public Image(String id, String name) { 
        this.id = id; 
        this.name = name; 
      } 
    } 

The preceding POJO class can be described as follows:

  • @Data is a Lombok annotation that generates getters, toString, hashCode, equals as well as setters for all non-final fields
  • @NoArgsConstructor is a Lombok annotation to generate a no-argument constructor
  • It has id and name fields for storing data
  • We have crafted a custom constructor to load up fields of data

With this simple data type, we can now focus on reactively interacting with them.

Nothing is simple without creating new data. To do that, we can write an HTTP POST operation as follows:

    @PostMapping(API_BASE_PATH + "/images") 
    Mono<Void> create(@RequestBody Flux<Image> images) { 
      return images 
       .map(image -> { 
         log.info("We will save " + image + 
          " to a Reactive database soon!"); 
          return image; 
       }) 
       .then(); 
    } 

The last code can be described as follows:

  • @PostMapping indicates this method will respond to HTTP POST calls. The route is listed in the annotation.
  • @RequestBody instructs Spring to fetch data from the HTTP request body.
  • The container for our incoming data is another Flux of Image objects.
  • To consume the data, we map over it. In this case, we simply log it and pass the original Image onto the next step of our flow.
  • To wrap this logging operation with a promise, we invoke Flux.then(), which gives us Mono<Void>. Spring WebFlux will make good on this promise, subscribing to the results when the client makes a request.

If we run this code and submit some JSON, we can check out the results.

First, let's use HTTPie (https://httpie.org):

http --json -v POST localhost:8080/api/images id=10 name=foo  

The verbose results are easy to read and are as follows:

POST /api/images HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 27
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/0.9.8
    
{
  "id": "10",
  "name": "foo"
}
    
HTTP/1.1 200
Content-Length: 0
Date: Sat, 28 Jan 2017 20:14:35 GMT  

In this case, HTTPie nicely sent a single item and our Spring WebFlux controller parsed it perfectly, like this:

... c.g.learningspringboot.ApiController ... We will save
Image(id=10, name=foo) to a Reactive database soon!

Single entry Flux has been nicely handled.

If we want to send a JSON array, we can either embed the JSON array in a file or just send it directly with curl, as follows:

curl -v -H 'Content-Type:application/json' -X POST -d '[{"id":10,
"name": "foo"}, {"id":11, "name":"bar"}]' localhost:8080/api/images

Ta-dah!

c.g.learningspringboot.ApiController ... We will save Image(id=10,
name=foo) to a Reactive database soon!
c.g.learningspringboot.ApiController ... We will save Image(id=11,
name=bar) to a Reactive database soon!
Whether we send a single JSON item or an array of JSON items, Spring WebFlux maps both onto Reactor's Flux with no issue. In classic Spring MVC, we'd have to choose either Image or List<Image> and encode things properly or write two handlers.

Want to dial up the log levels? With Spring Boot, adjusting logging levels is a snap. Rename the application.properties file supplied by start.spring.io as application.yml, and edit it to look like this:

    logging: 
      level: 
        io: 
          netty: DEBUG 
        reactor: DEBUG 

The preceding code will punch up Netty and Project Reactor to spit out DEBUG level messages.

If we fetch the list of images again (http localhost:8080/api/images), we can see stuff like this in the server logs:

    2017-01-28 15:46:23.470 DEBUG 28432 --- [ctor-http-nio-4] r.i.n.http.server.HttpServerOperations   : New http connection, requesting read
    2017-01-28 15:46:23.471 DEBUG 28432 --- [ctor-http-nio-4] r.ipc.netty.http.server.HttpServer       : [id: 0x9ddcd1ba, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:65529] RECEIVED: 145B
            +-------------------------------------------------+
            |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
    +--------+-------------------------------------------------+----------------+
    |00000000| 47 45 54 20 2f 61 70 69 2f 69 6d 61 67 65 73 20 |GET /api/images |
    |00000010| 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 |HTTP/1.1..Host: |
    |00000020| 6c 6f 63 61 6c 68 6f 73 74 3a 38 30 38 30 0d 0a |localhost:8080..|
    |00000030| 55 73 65 72 2d 41 67 65 6e 74 3a 20 48 54 54 50 |User-Agent: HTTP|
    |00000040| 69 65 2f 30 2e 39 2e 38 0d 0a 41 63 63 65 70 74 |ie/0.9.8..Accept|
    |00000050| 2d 45 6e 63 6f 64 69 6e 67 3a 20 67 7a 69 70 2c |-Encoding: gzip,|
    |00000060| 20 64 65 66 6c 61 74 65 0d 0a 41 63 63 65 70 74 | deflate..Accept|
    |00000070| 3a 20 2a 2f 2a 0d 0a 43 6f 6e 6e 65 63 74 69 6f |: */*..Connectio|
    |00000080| 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a 0d |n: keep-alive...|
    |00000090| 0a                                              |.               |
    +--------+-------------------------------------------------+----------------+
    2017-01-28 15:46:23.471 DEBUG 28432 --- [ctor-http-nio-4] r.ipc.netty.channel.ChannelOperations    : [HttpServer] handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter@3a950f21  

This shows the incoming web request to GET /api/images, headers and all. The output can also be read, but given the volume of data from Netty, its verbose output is not shown. Nevertheless, these log levels provide a handy means to debug traffic on the wire.

DON'T DO THIS if the request or the results are HUGE! I once switched this on when I was uploading a 300 MB JAR file. The logging broke the application.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.82.154