@RequestMapping là một trong những annotation phổ biến nhất được sử dụng trong các ứng dụng Web Spring. Annotation này ánh xạ các HTTP request tới các phương thức xử lý của MVC và REST controller.

Trong bài này, chúng ta sẽ thấy mức độ linh hoạt của annotation @RequestMapping khi được sử dụng để ánh xạ các phương thức xử lý trong Spring MVC controller.

Request Mapping cơ bản

Trong các ứng dụng Spring MVC, RequestDispatcher (Front Controller ở hình bên dưới) chịu trách nhiệm định tuyến các HTTP request đến tới các phương thức xử lý của controller.

Annotation @RequestMapping

Để định cấu hình ánh xạ các web request, chúng ta sử dụng annotation @RequestMapping.

Annotation @RequestMapping có thể được áp dụng cho cấp độ lớp và / hoặc cấp độ phương thức trong controller.

Annotation @RequestMapping cấp lớp ánh xạ một đường dẫn hoặc mẫu request cụ thể lên controller. Sau đó, ta có thể áp dụng các annotation @RequestMapping cấp phương thức bổ sung để làm cho ánh xạ cụ thể hơn đối với các phương thức xử lý.

Dưới đây là một ví dụ về Annotation @RequestMapping được áp dụng cho cả lớp và phương thức.

@RestController
@RequestMapping("/home")
public class IndexController {
    @RequestMapping("/")
    String get(){
        //mapped to hostname:port/home/
        return "Hello from get";
    }

    @RequestMapping("/index")
    String index(){
        //mapped to hostname:port/home/index/
        return "Hello from index";
    }
}

Với mã trước đó, các request đến “/home” sẽ được xử lý bởi phương thức xử lý get() trong khi request đến “/home/index” sẽ được xử lý bởi phương thức xử lý index().

@RequestMapping với nhiều URI

Chúng ta có thể có nhiều ánh xạ yêu cầu (request mapping) cho một phương thức. Chúng ta thêm một annotation @RequestMapping với một danh sách các giá trị.

@RestController
@RequestMapping("/home")
public class IndexController {

    @RequestMapping(value={"", "/page", "page*","view/*,**/msg"})
    String indexMultipleMapping(){
        return "Hello from index multiple mapping.";
    }
}

Như bạn có thể thấy trong mã này, @RequestMapping hỗ trợ khai báo một danh sách các giá trị cho thuộc tính value.

Các mẫu đường dẫn sau đây là hợp lệ:

  • localhost:8080/home
  • localhost:8080/home/
  • localhost:8080/home/page
  • localhost:8080/home/pageabc
  • localhost:8080/home/view/
  • localhost:8080/home/view/view

@RequestMapping với @RequestParam

Annotation @RequestParam được sử dụng với @RequestMapping để liên kết một tham số yêu cầu (request param) với tham số của phương thức xử lý.

Annotation @RequestParam có thể có hoặc không có giá trị. Giá trị chỉ định tham số yêu cầu cần được ánh xạ tới tham số phương thức xử lý, như được hiển thị trong đoạn mã sau:

@RestController 
@RequestMapping("/home")
public class IndexController {
 
    @RequestMapping(value = "/id") 
    String getIdByValue(@RequestParam("id") String personId){
        System.out.println("ID is "+personId);
        return "Get ID from query string of URL with value element";
    }
 
    @RequestMapping(value = "/personId") 
    String getId(@RequestParam String personId){
        System.out.println("ID is "+personId);
        return "Get ID from query string of URL without value element";
    }
}

Trong đoạn mã trên, tham số yêu cầu “id” sẽ được ánh xạ tới tham số “personId” của phương thức xử lý getIdByValue().

Sau đây là một mẫu đường dẫn hợp lệ:

localhost:8090/home/id?id=5

Giá trị của @RequestParam có thể được bỏ qua nếu tên của tham số yêu cầu và tên của tham số trong phương thức xử lý giống nhau (Như đối với phương thức xử lý getId trong ví dụ trên)

Sau đây là một mẫu đường dẫn hợp lệ :

localhost:8090/home/personId?personId=5

Phần tử bắt buộc của @RequestParam xác định xem giá trị tham số có bắt buộc hay không.

@RestController
@RequestMapping("/home")
public class IndexController {
    @RequestMapping(value = "/name")
    String getName(@RequestParam(value = "person", required = false)      String personName){
        return "Required element of request param";
    }
}

Trong đoạn mã này, do phần tử bắt buộc được chỉ định là false, phương thức xử lý getNam () sẽ được gọi cho cả hai URL này:

  • /home/name?person=xyz
  • /home/name

Giá trị mặc định của @RequestParam được sử dụng để cung cấp giá trị mặc định khi tham số yêu cầu không được cung cấp hoặc trống.

@RestController 
@RequestMapping("/home")
public class IndexController {
    @RequestMapping(value = "/name")
    String getName(@RequestParam(value = "person", defaultValue = "John") String personName ){
        return "Required element of request param";
    }
}

Trong mã này, nếu tham số yêu cầu “person” là trống, phương thức xử lý getName() sẽ nhận giá trị mặc định “John” như là tham số.

@RequestMapping với HTTP Method

Annotation @RequestMapping có khả năng xử lý các phương thức yêu cầu HTTP, chẳng hạn như GET, PUT, POST, DELETE và PATCH.

Theo mặc định, tất cả các yêu cầu được coi là loại HTTP GET.

Để xác định request mapping với một phương thức HTTP cụ thể, bạn cần khai báo phương thức HTTP trong @RequestMapping bằng cách sử dụng phần tử phương thức như sau.

@RestController
@RequestMapping("/home")
public class IndexController {
    @RequestMapping(method = RequestMethod.GET)
    String get(){
        return "Hello from get";
    }
    @RequestMapping(method = RequestMethod.DELETE)
    String delete(){
        return "Hello from delete";
    }
    @RequestMapping(method = RequestMethod.POST)
    String post(){
        return "Hello from post";
    }
    @RequestMapping(method = RequestMethod.PUT)
    String put(){
        return "Hello from put";
    }
    @RequestMapping(method = RequestMethod.PATCH)
    String patch(){
        return "Hello from patch";
    }
}

Trong đoạn mã trên, phần tử phương thức của các annotation @RequestMapping chỉ ra loại phương thức HTTP của yêu cầu HTTP.

Tất cả các phương thức xử lý sẽ xử lý các yêu cầu đến cùng một URL (/home), nhưng sẽ phụ thuộc vào phương thức HTTP được sử dụng.

Ví dụ: một yêu cầu POST đến “/home” sẽ được xử lý bằng phương thức post(). Trong khi một yêu cầu DELETE đến “/home” sẽ được xử lý bằng phương thức delete().

Bạn có thể thấy Spring MVC sẽ ánh xạ các phương thức khác bằng cách sử dụng logic này.

Sử dụng @RequestMapping với Producible and Consumable

Các kiểu request mapping có thể được giới hạn bằng cách sử dụng các phần tử produces và consumes của annotation @RequestMapping.

Để tạo đối tượng trong Media Type được yêu cầu, bạn sử dụng phần tử produces của @RequestMapping kết hợp với annotation @ResponseBody.

Bạn cũng có thể sử dụng đối tượng với Media Type được yêu cầu bằng cách sử dụng phần tử consumes của @RequestMapping kết hợp với annotation @RequestBody.

Mã nguồn để sử dụng produces và consumes với @RequestMapping như sau:

@RestController
@RequestMapping("/home")
public class IndexController {
    @RequestMapping(value = "/prod", produces = {"application/JSON"})
    @ResponseBody
    String getProduces(){
        return "Produces attribute";
    }

    @RequestMapping(value = "/cons", consumes = {"application/JSON",     "application/XML"})
    String getConsumes(){
        return "Consumes attribute";
    }
}

Trong mã nguồn này, phương thức xử lý getProduces() tạo ra dữ liệu kiểu JSON. Phương thức xử lý getConsume() tạo ra dữ liệu kiểu JSON cũng như XML.

@RequestMapping với Headers

Annotation @RequestMapping cung cấp headers để giới hạn request mapping dựa trên các header có trong request.

Chúng ta có thể chỉ định headers là “myHeader = myValue”.

@RestController 
@RequestMapping("/home")
public class IndexController {
    @RequestMapping(value = "/head", headers = {"content- type=text/plain"})
    String post(){ 
        return "Mapping applied along with headers";
    } 
}

Trong đoạn mã trên, thuộc tính headers của annotation @RequestMapping giới hạn ánh xạ vào phương thức post(). Với điều này, phương thức post() sẽ xử lý các yêu cầu đến “/home/head” có kiểu nội dung chỉ định văn bản thuần túy.

Chúng ta cũng có thể chỉ ra nhiều giá trị headers như thế này:

@RestController
@RequestMapping("/home")
public class IndexController {
    @RequestMapping(value = "/head", headers = {"content-  type=text/plain", "content-type=text/html"}) 
    String post(){
        return "Mapping applied along with headers";
    } 
}

Ở đây cả text/plain cũng như text/html đều được chấp nhận bởi phương thức xử lý post().

@RequestMapping với Request Parameters

Phần tử params của annotation @RequestMapping tiếp tục giúp giới hạn request mapping. Sử dụng phần tử params, bạn có thể có nhiều phương thức xử lý yêu cầu xử lý các yêu cầu cho cùng một URL, nhưng với các tham số khác nhau.

Bạn có thể định nghĩa params là myParams = myValue. Bạn cũng có thể sử dụng toán tử phủ định để chỉ định rằng một giá trị tham số cụ thể không được hỗ trợ trong request.

@RestController
@RequestMapping("/home")
public class IndexController {
    @RequestMapping(value = "/fetch", params = {"personId=10"})
    String getParams(@RequestParam("personId") String id){
        return "Fetched parameter using params attribute = "+id;
    }
    @RequestMapping(value = "/fetch", params = {"personId=20"})
    String getParamsDifferent(@RequestParam("personId") String id){
        return "Fetched parameter using params attribute = "+id;
    }
}

Trong đoạn mã này, cả hai phương thức getParams() và getParamsDifferent() sẽ xử lý các request đến cùng một URL (/home/fetch) nhưng sẽ thực thi tùy thuộc vào phần tử params.

Ví dụ: khi URL là “/home/fetch?Id=10”, phương thức xử lý getParams() sẽ được thực thi với giá trị id 10 .. Đối với URL, localhost:8080/home/fetch?PersonId = 20, phương thức xử lý getParamsDifferent( ) được thực thi với giá trị id 20.

@RequestMapping với Dynamic URIs

Annotation @RequestMapping được sử dụng kết hợp với annotation @PathVaraible để xử lý các URI động. Trong trường hợp sử dụng này, các giá trị URI có thể đóng vai trò là tham số của các phương thức xử lý trong controller. Bạn cũng có thể sử dụng biểu thức chính quy để chỉ chấp nhận các giá trị URI động khớp với biểu thức chính quy

@RestController
@RequestMapping("/home")
public class IndexController {
    @RequestMapping(value = "/fetch/{id}", method = RequestMethod.GET)
    String getDynamicUriValue(@PathVariable String id){
        System.out.println("ID is "+id);
        return "Dynamic URI parameter fetched";
    } 
    @RequestMapping(value = "/fetch/{id:[a-z]+}/{name}", method = RequestMethod.GET)
    String getDynamicUriValueRegex(@PathVariable("name") String name){
        System.out.println("Name is "+name);
        return "Dynamic URI parameter fetched using regex";
    } 
}

Trong mã này, phương thức getDynamicUriValue() sẽ thực thi một yêu cầu tới localhost:8080/home/fetch/10. Ngoài ra, tham số id của phương thức xử lý getDynamicUriValue() sẽ được điền với giá trị 10 một cách linh hoạt.

Phương thức getDynamicUriValueRegex() sẽ thực thi một yêu cầu tới localhost:8080/home/fetch/category/shirt. Tuy nhiên, một ngoại lệ sẽ được đưa ra cho yêu cầu “/home/fetch/10/shirt vì nó không khớp với biểu thức thông thường.

@PathVariable hoạt động khác với @RequestParam. Bạn sử dụng @RequestParam để lấy các giá trị của các tham số truy vấn từ URI. Mặt khác, bạn sử dụng @PathVariable để lấy các giá trị tham số từ mẫu URI.

@RequestMapping Default Handler Method

Trong lớp controller, chúng ta có thể có phương thức xử lý mặc định được thực thi khi có yêu cầu cho URI mặc định.

Dưới đây là một ví dụ về một phương thức xử lý mặc định

@RestController
@RequestMapping("/home")
public class IndexController {
    @RequestMapping()
    String default(){ 
        return "This is a default method for the class";
    }
}

Trong mã này, một yêu cầu đến “/home” sẽ được xử lý theo phương thức default() vì annotation không chỉ định bất kỳ giá trị nào.

@RequestMapping Shortcuts

Spring 4.3 đã giới thiệu các biến thể ở mức phương thức, còn được gọi là các annotation tổng hợp của @RequestMapping. Các annotation này thể hiện tốt hơn các ngữ nghĩa của các phương thức xử lý. Chúng hoạt động như một trình bao bọc cho @RequestMapping và đã trở thành các cách tiêu chuẩn để xác định các điểm cuối.

Ví dụ: @GetMapping là một annotation tổng hợp hoạt động như một lối tắt cho @RequestMapping (method = RequestMethod.GET).

Các biến thể mức phương thức là:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

Các mã sau đây cho thấy bằng cách sử dụng các annotation này:

@RequestMapping("/home")
public class IndexController {
    @GetMapping("/person")
    public @ResponseBody ResponseEntity<String> getPerson() {
        return new ResponseEntity<String>("Response from GET", HttpStatus.OK);
    }
 
    @GetMapping("/person/{id}")
    public @ResponseBody ResponseEntity<String>.    getPersonById(@PathVariable String id){
        return new ResponseEntity<String>("Response from GET with id " +id,HttpStatus.OK);
     }
 
    @PostMapping("/person")
    public @ResponseBody ResponseEntity<String> postPerson() {
        return new ResponseEntity<String>("Response from POST method", HttpStatus.OK);
    }
 
    @PutMapping("/person")
    public @ResponseBody ResponseEntity<String> putPerson() {
        return new ResponseEntity<String>("Response from PUT method", HttpStatus.OK);
    }
 
    @DeleteMapping("/person")
    public @ResponseBody ResponseEntity<String> deletePerson() {
        return new ResponseEntity<String>("Response from DELETE method", HttpStatus.OK);
    }
 
    @PatchMapping("/person")
    public @ResponseBody ResponseEntity<String> patchPerson() {
        return new ResponseEntity<String>("Response from PATCH method", HttpStatus.OK);
    }
}

Trong mã này, mỗi phương thức xử lý được chú thích với các biến thể cấu thành của @RequestMapping.

Kết luận

Như bạn có thể thấy trong bài viết này, annotation @RequestMapping rất linh hoạt. Bạn có thể sử dụng annotation này để cấu hình Spring MVC để xử lý nhiều trường hợp sử dụng. Nó có thể được sử dụng để cấu hình các yêu cầu trang web truyền thống và các dịch vụ web RESTFul trong Spring MVC.

Author: Nguyễn Khánh Tùng

Đăng ký nhận bộ tài liệu học Java trên 2 trang giấy tại đây

Xem thêm: tái cấu trúc mã nguồn: chuẩn mã hóa