What is and How to Develop a (Http)Controller on Back-End Applications

Petros Koulianos šŸ’€ā˜ šŸ‘½
3 min readSep 11, 2022

Responsibilities, information hiding, dependencies, exception handling, logging on exceptions of a Controller

Controllers on backend apps REST-full or MPAā€™s (Multi Page Applications) are usually the first place we start to write code.

But what code can we write on controllers?

This post is originally posted on https://petran.substack.com

Responsibilities

Controllers are usually functions our appā€™s framework invokes on specific URL paths and HTTP methods.

//Spring Boot Sample function on a HttpController
// GET HTTP request ON `/blogs' path
@GetMapping(value = "/blogs")
public Blogs getBlogs() {
}

Controllers belong to the app layer on a layer architecture pattern

A controller handles the HTTP request extracts any data from it and determines the response data and HTTP status code.

1ļøāƒ£The main responsibility is to control and handle the HTTP request.

A better name is HttpController this indicates directly the form of the context that the controller controls

2ļøāƒ£The second responsibility is to validate and normalize (if needed) the data from the HTTP request. We must always validate the request data for security reasons and more.

3ļøāƒ£The third critical responsibility is to invoke the business case the HTTP endpoint is built for, for example creating a new user, updating a post content, etc.

4ļøāƒ£The last responsibility is to serve the same response schema by HTTP code, changing the response schema is bad practice.

Information Hiding

Each HttpController serves some data(model) and a client integrates it.

This means that when a client integrates with the model the controller is responsible to serve it constantly (if not issues will come up sooner or later).

HttpControllers have to expose only the data that can be maintained and only the needed data, not less or more.

Exposing unnecessary data can lead to serve unmaintained data, less flexibilty, less options to migrate, refactor or redesign internal code blocks

Dependencies

HttpControllers have to work on specific tasks and to do it they must have the proper dependencies.

The best way to handle dependencies is with DI (Dependency Injection) with the help of a framework.

DI frameworks will help to inject quick and easy dependencies directly on the constructor or the function (depending on the framework capabilities).

//Spring Boot Sample HttpController with DI@RestController
class BlogController {
private final BlogRepository repository; BlogController(BlogRepository repository) {
this.repository = repository;
}
@GetMapping("/blogs")
List<Blog> all() {
return repository.findAll();
}

Try to avoid creating services, repositories, utilities, or any other complex objects directly on a HttpController, it will generate boilerplate code.

From a layer architecture pattern scope, a HttpController can get dependencies from the domain or infra layer.

Exception Handling

Exceptions on a HttpController are a great way to handle and determine the response HTTP code and data.

For example :

  1. If the data validation fails and throws a proper exception we can catch it and return something like an HTTP 400 bad request and a message to indicate the reason for the badly formatted data.
  2. If the main business case fails for a specific reason we can catch the exception and return an HTTP 500 internal server error and a message to indicate the reason that the code fails.
@GetMapping("/blogs")
List<Blog> all()
{
try{
return repository.findAll();
}
catch (InfraLayerException exc) {
throw new ResponseStatusException(
HttpStatus.INTERNAL_SERVER_ERROR, "Error on Infra Layer, ", exc);
}
}

Logging on Exceptions

Logging on a HttpControllerā€™s exceptions is a great way to eliminate duplicated logging messages using an Exception Bubbling approach.

Prefer to log your exception message before returning the HTTP response, we can also add some extra data such as the class and the function name for more detailed logging messages and easier debugging.

@GetMapping("/blogs")
List<Blog> all()
{
try{
return repository.findAll();
}
catch (InfraLayerException exc) {
Logger.error('[ClassName.functionName] :: '+exc.getMessage()); throw new ResponseStatusException(
HttpStatus.INTERNAL_SERVER_ERROR, "Error on Infra Layer, ", exc);
}
}

Summary

  • Extract and validate request data with the help of a utility service
  • Invoke the main business case
  • Handle any exceptions if needed
  • Log exception messages
  • Return the response data with a proper HTTP status code

--

--

Petros Koulianos šŸ’€ā˜ šŸ‘½

Software Engineer šŸ‘½ | Building applications for health industry | Work with JavaScript, Typescript, PHP | My NewsletteršŸ“© at petran.substack.com