Ok, I’m back here. It’s been a bit busy lately, so the post took a long time, I hope you sympathize
Today we will together “dissect” inside the controller how and how it works. Ok let’s go.
1. What is Controller, how does it work?
1.1. What is Controller?
As mentioned in previous articles, the Controller in the Spring Boot application is the place to receive the request and return the response to the client. It can be understood that the controller is the middle layer between your server and the outside.
In terms of code, Controller is merely a bean marked with @Controller
or @RestController
.
In Spring Boot, there are two types of Controller, corresponding to the two above annotations:
@Controller
can return View via a String or JSON data in the response body (if specified). Suitable for controllers with routing, turning pages of all types.@RestController
can only return data in the response body. Suitable for controllers to provide APIs.
Hence, we could say @RestController
= @Controller
+ @ResponseBody
.
1.2. Code examples
Below is the structure of a controller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token annotation punctuation">@Controller</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">HomeController</span> <span class="token punctuation">{</span> <span class="token comment">// Bên trong controller sẽ có nhiều method, mỗi cái sẽ bắt request cụ thể</span> <span class="token comment">// Bắt GET /home request và trả về view</span> <span class="token annotation punctuation">@GetMapping</span> <span class="token punctuation">(</span> <span class="token string">"/home"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">home</span> <span class="token punctuation">(</span> <span class="token class-name">Model</span> model <span class="token punctuation">)</span> <span class="token punctuation">{</span> model <span class="token punctuation">.</span> <span class="token function">addAttribute</span> <span class="token punctuation">(</span> <span class="token string">"name"</span> <span class="token punctuation">,</span> <span class="token string">"John"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token string">"index"</span> <span class="token punctuation">;</span> <span class="token comment">// Return tên của View, model sẽ tự động pass vào view</span> <span class="token punctuation">}</span> <span class="token comment">// Hoặc có thể trả về data trong response body (như các API)</span> <span class="token annotation punctuation">@GetMapping</span> <span class="token punctuation">(</span> <span class="token string">"/users"</span> <span class="token punctuation">)</span> <span class="token annotation punctuation">@ResponseBody</span> <span class="token keyword">public</span> <span class="token class-name">List</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">User</span> <span class="token punctuation">></span></span> <span class="token function">getUserList</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token punctuation">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Hoặc cái này tương tự như trên, nhưng có thể tùy chỉnh response status code, header,...</span> <span class="token annotation punctuation">@GetMapping</span> <span class="token punctuation">(</span> " <span class="token operator">/</span> users <span class="token operator">/</span> <span class="token punctuation">{</span> id <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token class-name">ResponseEntity</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">User</span> <span class="token punctuation">></span></span> <span class="token function">getUserById</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@PathVariable</span> <span class="token punctuation">(</span> <span class="token string">"id"</span> <span class="token punctuation">)</span> <span class="token class-name">String</span> userId <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Không cần @ResponseBody do có body rồi</span> <span class="token keyword">return</span> <span class="token class-name">ResponseEntity</span> <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">body</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">User</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
1.3. Controller actions
As shown above, when the client sends a request to its Spring Boot server, it goes through what is called the Front controller first. Here is the controller available, it has the following effect:
- Resolution request, find out whether the request calls to which controller’s method to properly call there
- The data of the request will be
@RequestParam
and@PathVariable
accordingly to the controller method parameters (with@RequestParam
,@PathVariable
,@Header
, … respectively). - In particular, Spring MVC can parse complex data such as enum, List or object. For example, the enum in the request is a string, which will still be properly parsed into the enum.
- If the data cannot be parsed, the front controller will return a bad request (or have another mechanism for us to override this).
It is the same with the opposite direction. The data returned from the controller will be built into a response and returned to the client.
2. Controller mapping
2.1. Types of HTTP requests
Anyone who learns about the web must already know the concept of HTTP requests. I will not talk in depth about this part, but roughly understand that each HTTP request will contain 2 important information:
- The request to which URL (where the request is)
- What is an HTTP method (does something to do with the URL)
In the controller, just grasp the two above information, it will capture all incoming requests, then continue processing.
In Rest API design, it is common to use nouns in URLs to refer to the impacted object. And the HTTP methods to represent the action will apply to that object.
Eg:
- Request to
GET /users
has an object of actionusers
(all users), and the action isGET
(get information). - Request to
PUT /users/123
has objectusers/123
(user code is 123) and action isPUT
(update information)
It is generally recommended that one use the correct HTTP method with the corresponding CRUD actions:
- Create: use the POST method
- Read: use GET method
- Update: use the PUT method
- Delete: use the DELETE method
Most web applications are using the 4 basic CRUD actions above 2/3 already. Also there can be other actions without the corresponding method, like login, you can add endpoint like POST /login
(it is safer to use POST, read more about HTTP methods to better understand their meaning. ).
2.2. Catch the requests
Spring Boot uses the following annotations, marking each of the controller’s methods , to specify that when the corresponding HTTP method is called, the method will be executed.
1 2 3 4 5 6 7 8 9 | <span class="token annotation punctuation">@RestController</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserController</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@GetMapping</span> <span class="token punctuation">(</span> <span class="token string">"/users"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token class-name">ResponseEntity</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token operator">?</span> <span class="token punctuation">></span></span> <span class="token function">getAllUsers</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@DeleteMapping</span> <span class="token punctuation">(</span> <span class="token string">"/users/{id}"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">deleteUser</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@PathVariable</span> <span class="token punctuation">(</span> <span class="token string">"id"</span> <span class="token punctuation">)</span> <span class="token keyword">int</span> id <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The above example has 2 methods, which capture two requests, respectively GET /users
and DELETE /users/{id}
. When there is a corresponding request sent, the above two methods will execute and return the results to the client.
Common annotations like @GetMapping
, @PostMapping
, @PutMapping
, … take the form of the HTTP method name plus the word “mapping”. You can also use @RequestMapping
and specify the method
attribute as follows.
1 2 | <span class="token annotation punctuation">@RequestMapping</span> <span class="token punctuation">(</span> value <span class="token operator">=</span> <span class="token string">"/users"</span> <span class="token punctuation">,</span> method <span class="token operator">=</span> <span class="token class-name">RequestMethod</span> <span class="token punctuation">.</span> GET <span class="token punctuation">)</span> |
In addition, @RequestMapping
can also be used above the controller class, to specify the root endpoint for all methods within it. The example is as follows.
1 2 3 4 5 6 7 | <span class="token annotation punctuation">@RestController</span> <span class="token annotation punctuation">@RequestMapping</span> <span class="token punctuation">(</span> <span class="token string">"/users"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserController</span> <span class="token punctuation">{</span> <span class="token comment">// Kết hợp với route gốc ở trên, ta có /users/info</span> <span class="token annotation punctuation">@GetMapping</span> <span class="token punctuation">(</span> <span class="token string">"/info"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
3. Receive request data in Controller
The controller receives data from the request, depending on where the data is located, we have different ways of retrieving:
- Request param (query string)
- Path variable
- Request body
- Header
3.1. Request param (query string)
For example, the following request GET /users?age=18&name=Dũng
, we have 2 request params age = 18
and name = Dũng
. Then, if we want to get the above two values, we use @RequestParam
as follows.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token annotation punctuation">@RestController</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserController</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token annotation punctuation">@GetMapping</span> <span class="token punctuation">(</span> <span class="token string">"/users"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token class-name">ResponseEntity</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token operator">?</span> <span class="token punctuation">></span></span> <span class="token function">getAllUsers</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@RequestParam</span> <span class="token punctuation">(</span> <span class="token string">"age"</span> <span class="token punctuation">)</span> <span class="token keyword">int</span> age <span class="token punctuation">,</span> <span class="token annotation punctuation">@RequestParam</span> <span class="token punctuation">(</span> <span class="token string">"name"</span> <span class="token punctuation">)</span> <span class="token class-name">String</span> name <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Lúc này hai biến age và name đã có dữ liệu tương ứng</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
In case @RequestParam
has other parameters, we have to write the following. For example, both the age
and name
fields above are optional, which is optional, so we use the required = false
attribute for @RequestParam
(default is true).
1 2 3 4 5 6 7 8 9 10 11 | <span class="token annotation punctuation">@RestController</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserController</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token annotation punctuation">@GetMapping</span> <span class="token punctuation">(</span> <span class="token string">"/users"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token class-name">ResponseEntity</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token operator">?</span> <span class="token punctuation">></span></span> <span class="token function">getAllUsers</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@RequestParam</span> <span class="token punctuation">(</span> value <span class="token operator">=</span> <span class="token string">"age"</span> <span class="token punctuation">,</span> required <span class="token operator">=</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token class-name">Integer</span> age <span class="token punctuation">,</span> <span class="token annotation punctuation">@RequestParam</span> <span class="token punctuation">(</span> value <span class="token operator">=</span> <span class="token string">"name"</span> <span class="token punctuation">,</span> required <span class="token operator">=</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token class-name">String</span> name <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Lúc này hai biến age và name đã có dữ liệu tương ứng</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
At this point, since the age
variable may not be present, we must give it an Integer
with a value of null to know whether age
was sent or not. If it is primitive then it will always have a default value.
Also @RequestParam
also has the defaultValue
property, if the request is not specified then the default value will be used.
3.2 Path variable
Path variable is part of the URL path, for example GET /users/123/info
then 123
is path variable. Use @PathVariable
to do this, similar to using @RequestParam
.
1 2 3 4 5 6 | <span class="token annotation punctuation">@GetMapping</span> <span class="token punctuation">(</span> <span class="token string">"/users/{id}/info"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token class-name">ResponseEntity</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token operator">?</span> <span class="token punctuation">></span></span> <span class="token function">getUserInfo</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@PathVariable</span> <span class="token punctuation">(</span> value <span class="token operator">=</span> <span class="token string">"id"</span> <span class="token punctuation">,</span> defaultValue <span class="token operator">=</span> <span class="token string">"0"</span> <span class="token punctuation">)</span> <span class="token keyword">int</span> userId <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// id là tên path variable, tương ứng trên url {id}</span> <span class="token punctuation">}</span> |
@PathVariable
also has the same properties @RequestParam
.
3.3. Request body
Request method PUT, POST new request body, this is the place to store the main data to send. Often the request body will be in the form of JSON or form-data, when entering the controller will be automatically parsed into an Object (eg DTO).
1 2 3 4 5 6 | <span class="token annotation punctuation">@PostMapping</span> <span class="token punctuation">(</span> <span class="token string">"/login"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token class-name">ResponseEntity</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token operator">?</span> <span class="token punctuation">></span></span> <span class="token function">login</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">LoginDto</span> loginDto <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Dữ liệu trong request body có thể là JSON, form-data,...</span> <span class="token comment">// Tuy nhiên khi vào controller sẽ bị parse thành object hết</span> <span class="token punctuation">}</span> |
Here is an example of the LoginDto
class above (using lombok).
1 2 3 4 5 6 7 | <span class="token annotation punctuation">@Getter</span> <span class="token annotation punctuation">@Setter</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">LoginDto</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">String</span> username <span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">String</span> password <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
3.4. Header
1 2 3 4 5 | <span class="token annotation punctuation">@PostMapping</span> <span class="token punctuation">(</span> <span class="token string">"/login"</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token class-name">ResponseEntity</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token operator">?</span> <span class="token punctuation">></span></span> <span class="token function">login</span> <span class="token punctuation">(</span> <span class="token annotation punctuation">@Header</span> <span class="token punctuation">(</span> <span class="token string">"Authorization"</span> <span class="token punctuation">)</span> <span class="token class-name">String</span> authHeader <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Biến authHeader sẽ có giá trị là giá trị của Authorization header</span> <span class="token punctuation">}</span> |
For example, I want to perform user authentication using Basic authentication. The username and password information is encoded in the header named Authorization. Want to get the value in the header, then you use @Header
as above.
Okay here is the end of the post. The next article I will talk about how to return data back to the client. Remember to watch and do not forget to support me by voting and clips a lot.