12/08/2018, 15:40

Spring 5: Function Web Framework

Hôm nay mình sẽ giới thiệu về một trong những điểm mới của Spring 5. Đó chính là HandlerFunction, RouterFunction, and FilterFunction. Bình thường khi làm việc với Spring của các phiên bản trước, nếu muốn handle các request thì ta dùng các annotation quen thuộc của Spring là @Controller, ...

Hôm nay mình sẽ giới thiệu về một trong những điểm mới của Spring 5. Đó chính là HandlerFunction, RouterFunction, and FilterFunction. Bình thường khi làm việc với Spring của các phiên bản trước, nếu muốn handle các request thì ta dùng các annotation quen thuộc của Spring là @Controller, @RequestMapping. Tuy nhiên, với Spring 5 ta sẽ có thêm cách handle khác, trước hết ta xem ví dụ dứới đây, ở đây là ví dụ về việc các API expose ra Person object

public interface PersonRepository {
  Mono<Person> getPerson(int id);
  Flux<Person> allPeople();
  Mono<Void> savePerson(Mono<Person> person);
}

Về mặt structure có vẻ như khá giống với cách truyền thống, ngoại trừ 1 vài điểm mới lạ. Ta cùng đi giải thích các điểm mới lạ này. Flux<Person> sẽ trả về một List<Person> như truyền thống, and Mono<Person> sẽ trả về một object Person. Mono<Void> sẽ tương đương với void trong phương pháp truyền thống.

Dưới đây là cách mà ta expose mọi thứ ở controller với việc dùng cách mới của Spring 5:

RouterFunction<?> route = route(GET("/person/{id}"),
  request -> {
    Mono<Person> person = Mono.justOrEmpty(request.pathVariable("id"))
      .map(Integer::valueOf)
      .then(repository::getPerson);
    return Response.ok().body(fromPublisher(person, Person.class));
  })
  .and(route(GET("/person"),
    request -> {
      Flux<Person> people = repository.allPeople();
      return Response.ok().body(fromPublisher(people, Person.class));
    }))
  .and(route(POST("/person"),
    request -> {
      Mono<Person> person = request.body(toMono(Person.class));
      return Response.ok().build(repository.savePerson(person));
    }));

Sau khi start server và access http://localhost:8080/person/1 thì ta được kết quả sau

{
"name": "Tam Nguyen",
"age": 29
}

Trên đây là phần giới thiệu về một ví dụ cách handle request một cách mới lạ với Spring 5 Tiếp theo ta cùng nhau đi sau vào chi tiết.

Như đã nói ở trên thì Spring 5 có 3 component quan trọng về mảng request handler, chính là HandlerFunction, RouterFunction, and FilterFunction.

HandlerFunction

Đây thực tế chính là một bản sao của @RequestMapping. Một ví dụ rất đơn giản về handler function với ví dụ huyền thoại HelloWorld:

HandlerFunction<String> helloWorld =
  request -> Response.ok().body(fromObject("Hello World"));

Ngoài ra, theo như ví dụ về PersonRepository ở trên, thì HandlerFunction hỗ trợ đầy đủ các reactive như Flux (list of object), Mono (single object) hay đơn giản là Void (void).

RouterFunction

RouterFunction có chức năng tương tự như một @RequestMapping annotation. Tuy nhiên, có một sự khác biệt quan trọng: với annotation thì request của ta bị hạn chế đối với những gì có thể được thể hiện thông qua các giá trị của annotation, và việc xử lý chúng không phải là không thể override; còn với router function thì code xử lý là ngay trước mặt bạn: bạn có thể override hoặc replace nó khá dễ dàng.

Dưới đây là một ví dụ về một router function với một chức năng xử lý in-line. Nó có vẻ hơi rờm rà, nhưng đừng lo lắng về điều đó: chúng ta sẽ tìm cách để làm cho nó ngắn hơn ở phần dưới.

RouterFunction<String> helloWorldRoute = 
  request -> {
    if (request.path().equals("/hello-world")) {
      return Optional.of(r -> Response.ok().body(fromObject("Hello World")));
    } else {
      return Optional.empty();
    }
  };

Thông thường, bạn không viết các router function hoàn chỉnh mà đúng hơn là import RouterFunctions.route(), cái mà cho phép bạn tạo RouterFunction sử dụng một RequestPredicate (i.e. Predicate<Request>) và một HandlerFunction. Nếu mà Predicate đc apply thì handler function sẽ return, nếu không thì sẽ empty. Xem ví dụ về HelloWorld dưới đây             </div>
            
            <div class=

0