DEV Community

Cover image for 6 Responsibilities Of Controllers (Spring As An Example)
Abdulcelil Cercenazi
Abdulcelil Cercenazi

Posted on

6 Responsibilities Of Controllers (Spring As An Example)

What Is A Controller Anyways? 🧐

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.

How It's Done?👀

  • 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  
  }  
}
Enter fullscreen mode Exit fullscreen mode

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).


What Are The Responsibilities Of A Controller?☝️

Let's list them in order from the moment a request is received to the moment of returning a response.

1️⃣ Listening to HTTP requests👂🏼

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}.

2️⃣ Deserializing the input from the incoming request ✍️

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  
  }  
}
Enter fullscreen mode Exit fullscreen mode

The Grade class

class Grade{  
  Integer id;  
  String letter;  
  // getters and setters and constructors
}
Enter fullscreen mode Exit fullscreen mode

In this example, turn the POST request body into a Grade object.
The POST requests body should something like this:

{
    "id": "1",
    "letter": "A+"
}
Enter fullscreen mode Exit fullscreen mode

3️⃣ Validating the Deserialized objects🧙

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.

We can do the validation manually

@RestController  
public class GradeController {  
    @PostMapping("/grade/add")  
    public void getGradeById(@RequestBody Grade grade) throws Exception{  
        if (grade.getId() < 0)
            throw new Exception();
  }  
}
Enter fullscreen mode Exit fullscreen mode

Or we can use Spring's validation mechanisms

The Grade class with the validation annotation

class Grade{
  @Min(1) 
  Integer id;  
  String letter;
}
Enter fullscreen mode Exit fullscreen mode

The controller with the annotation to activate the validation

@PostMapping("/grade/add")  
public void getGradeById(@RequestBody @Validated Grade grade)  
{  
    // business stuff  
}
Enter fullscreen mode Exit fullscreen mode

Learn more about Spring validation from my other blog post.👈


4️⃣ Calling business logic 💼

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();  
  }  
}
Enter fullscreen mode Exit fullscreen mode

Note the usage of Lombok to write cleaner code. Check out my bog post about Lombok here.👈🏻


5️⃣ Serializing the output of the controller function 🛠

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;  
}
Enter fullscreen mode Exit fullscreen mode

So the response will be a 200 with a JSON value of

{
  "message" : "success"
}
Enter fullscreen mode Exit fullscreen mode

6️⃣ Handle Exceptions ⛔️

If an exception happens and goes unhandled by the business logic code, the controller translates it into a meaningful response.


Important Note ☢️

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.

Latest comments (1)

Collapse
 
matahariramadhan profile image
Matahari Ramadhan

Great article, thank you abdulcelil for sharing.