Dariusz Dudziński
Dariusz Dudziński

Reputation: 197

Proper way to write a controller

I am wondering what is the best way to build messages in controller based on a service result, to the front end. I am thinking in java/spring context using rest controllers, but it reflects the MVC model as well. Obviously the examples I have seen on the internet are way to simple to understand what are best practices in real world application. So, to the case, let's assume a following scenario:

  1. We have an controller CalculateController with a single method doMath(int a, int b)
  2. We have a service CalculatorService that perform the math it self in the doMath(int a, int b) method.
  3. We want to perform additional validation on input parameters. The method is called validateInput(int a, int b).
  4. We want to send as a response calculation result, or a specific information, why it failed.

So now the questions:

  1. As a result of controller method execution, I would retuun a Map of objects, that will be parsed into JSON. Possible entities would be:

    { "result":"12" }

    { "errorCode":"incorrect parameters" }

is this correct, or should I aim into different error handling? I know that I could additionally use a http error codes for example 400, or 406 to indicate that input parameters are incorrect, but personally I prefer not to use http error codes to indicate application logic result. (I consider input validation as appliaciton logic)

  1. Where the validateInput method should be placed, in controller, or in service?
  2. In the validateInput method I want to check two simplified conditions

a) is a < b and then inform user with a message "a cannot be smaller than b"

b) is a*a == b and then inform user with a message "a squared cannot be equal to b".

How should I provide the additional message based on the result? I can think of two solutions:

Upvotes: 0

Views: 1575

Answers (2)

rpieniazek
rpieniazek

Reputation: 2370

Spring supports custom validation. There is an annotation @Valid. You can add it before your parameter in Controller. I'll show it on the example. Firstly, create class with implements Validator interface:

@Component
public class MathValidator implements Validator {

    @Override
    public boolean supports(Class<?> aClass) {
        return MatchRequest.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
        MatchRequest request = (MatchRequest) o;

        if (request.getValue() == null) {
            errors.rejectValue("value", "Value cannot be empty");
        }
       //add another validation logic here.
    }
}

Then, in your Controller you can inject your validator like this:

@Autowired
private MathValidator validator;

and add this method with annotation, to show spring which form it should validate. Name in annotation is name of the parameter in controller method.

@InitBinder("request")
private void initBinder(WebDataBinder binder) {
    binder.setValidator(routeValidator);
}

Lets focus on the controller method. We have here parameter annotated with @Valid. Spring will call validate method from the class you've already implemented. It is obligatory to have the second parameter:Binding result.

@RequestMapping(value = "/math", method = RequestMethod.POST)
        public ResponseEntity calculate(@RequestBody @Valid MatchRequest request, BindingResult result) {
            if (result.hasErrors()) {
                return new ResponseEntity(result.getAllErrors(), HttpStatus.BAD_GATEWAY);
            }

            //call service,etc.
        }  

Also, there is another interesting tool in spring. It's annotation @ExceptionHandler. With this annotation, you can map exceptions, thrown from controller, or service to http requests. Read more on this topic on the official website

Upvotes: 1

Kirinya
Kirinya

Reputation: 245

About your question 2 & 3, you might want to look into the Validator interface: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html

This post shows how is is used: Spring MVC: How to perform validation?

As for question 1, you can transform the errors in the BindingResult to a JSON and use this on the client-side to perform useful feedback to the user.

Also, in my opinion, you should not use a HTTP failure code in that case because a user input error is not really an error, but a normal use case in your application.

Upvotes: 1

Related Questions