23
6 Responsibilities Of Controllers (Spring As An Example)
It acts as the entry point to our backend application from the outside world.
By outside world I mean
- Frontend applications that run on browsers.
- Other backend applications that run on servers.
- The controller defines entry points that clients from the outside world can talk with using REST operations (GET, POST, DELETE, etc..).
- A request that has the matching properties defined by a certain entry point will trigger the function of that entry point.
- The function then calls business logic functions that will do some stuff and return a response to the client.
@RestController
public class GradeController {
@GetMapping("/grade/{id}")
public void getGradeById(@PathVariable String id){
// business stuff
}
}
The example above describes an entry point at baseUrl/grade/someGradeId
that is run when a GET request is made with a URL matching the pattern (baseUrl/grade/1
for example).
Let's list them in order from the moment a request is received to the moment of returning a response.
What URL/HTTP Types/Request Parameters should trigger this function?
- In the example above, we are saying that the function getGradeById will be run when a GET request is made to the url
baeUrl/grade/{id}
.
Turn the request body into Java objects. Turn the request parameters into Integers, Strings, etc...
@RestController
public class GradeController {
@PostMapping("/grade/add")
public void getGradeById(@RequestBody Grade grade){
// business stuff
}
}
The Grade class
class Grade{
Integer id;
String letter;
// getters and setters and constructors
}
In this example, turn the POST request body into a Grade object.
The POST requests body should something like this:
{
"id": "1",
"letter": "A+"
}
We can specify validation rules against the inputs our controllers take. An error will be thrown if the rules weren't met.
For example, the id parameter shouldn't be negative.
@RestController
public class GradeController {
@PostMapping("/grade/add")
public void getGradeById(@RequestBody Grade grade) throws Exception{
if (grade.getId() < 0)
throw new Exception();
}
}
The Grade class with the validation annotation
class Grade{
@Min(1)
Integer id;
String letter;
}
The controller with the annotation to activate the validation
@PostMapping("/grade/add")
public void getGradeById(@RequestBody @Validated Grade grade)
{
// business stuff
}
Learn more about Spring validation from my other blog post.👈
The body of the controller function is executed. It should ideally call functions from the service layer which runs business code.
@RestController @RequiredArgsConstructor
public class GradeController {
private final GradeService gradeService;
@GetMapping("/grade")
public void getGradeById()
{
gradeService.doThings();
}
}
Note the usage of Lombok to write cleaner code. Check out my bog post about Lombok here.👈🏻
The returned value from the function is is turned into a HTTP response.
@RestController
public class GradeController {
@GetMapping("/grade/add")
public APIResponse getGradeById()
{
// do things
return new APIResponse("success");
}
}
@AllArgsConstructor
class APIResponse{
String message;
}
So the response will be a 200 with a JSON value of
{
"message" : "success"
}
If an exception happens and goes unhandled by the business logic code, the controller translates it into a meaningful response.
As we've seen, the controller has a lot to do! It's very important not to include business logic code in the controller.
I've seen this mistake done many times. I've also seen controllers with thousands of lines of business code.
This leads to creating a timed bomb that could explode on a Monday at 9 PM 💣
This post was inspired from this amazing post.
23