0

I have the following POJO:

@Entity // This tells Hibernate to make a table out of this class
public class User {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;


    private String name;

    private String email;

    private String username;

    private String password;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) throws DataFormatException {
        if(name.equals(""))
        {
            throw new DataFormatException("Mpla Mpla");
        }
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {

        this.email = email;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @JsonCreator
    public User(@JsonProperty("name") String name, @JsonProperty("email") String email,@JsonProperty("username") String username,@JsonProperty("password") String password) throws DataFormatException {
        setName(name);
        this.email = email;
        this.username = username;
        this.password = password;
}
    } 

And the following controller:

public class MainController {
    @Autowired // This means to get the bean called userRepository
    // Which is auto-generated by Spring, we will use it to handle the data
    private UserRepository userRepository;
    }
    @PostMapping(path="/add") // Map ONLY POST Requests
    public @ResponseBody String addNewUser (@RequestBody @Valid User user1) {
        // @ResponseBody means the returned String is the response, not a view name

        userRepository.save(user1);
        return "Saved";
    }

    @GetMapping(path="/all")
    public @ResponseBody Iterable<User> getAllUsers() {
        // This returns a JSON or XML with the users
        return userRepository.findAll();
    }

The problem is that instead of producing the error DataFormatException it produces:

"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
    "message": "JSON parse error: Can not construct instance of hello.Users.User, problem: Mpla Mpla; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of hello.Users.User, problem: Mpla Mpla\n at [Source: java.io.PushbackInputStream@7604dc21; line: 6, column: 1]",

Although as can be seen above the problem is correct but the message is wrong. So what can be done in order to produce the wanted error and not the one produced by Jackson?

J.Doo
  • 153
  • 3
  • 10
  • what will happen if you add a constructor without any argument? – Sal-laS Dec 21 '17 at 08:19
  • I need a constructor with arguments in order to validate the input. – J.Doo Dec 21 '17 at 08:20
  • The exception is thrown by spring's message converter. Which convert `json` to Object. Meanwhile when converting if any kind of exception is thrown then it will wrapped from converter. so generally it is thrown as `org.springframework.http.converter.HttpMessageNotReadableException`! – Zico Dec 21 '17 at 08:21
  • @Zico thank you! Although i still need to throw my exception so the person who consumes it knows what is going on.. How can i handle this situation? – J.Doo Dec 21 '17 at 08:27
  • Exception throwing is not good design in REST design and it is difficult to understand from client side. It is better to return custom JSON response including message and code not exception! – Zico Dec 21 '17 at 09:10
  • @Salman Also even if i add constructor with no arguements or no constructor at all setters and getters are auto found. SO the output is the same as above – J.Doo Dec 21 '17 at 09:11
  • @Zico So best here is to let the object be created, initialize 2 variables `error _code` and `message` into the coresponding `setter` and return them to client from `controller`? – J.Doo Dec 21 '17 at 09:12
  • No. The response having a wrapper which contain code, message and actually response object. You will set the custom code(ENUM) and message which will also used by client to get response. So every time you will return this generic response object. Whenever some error happen you return will error code and message only. In this case you let the object creation with invalid data. But then check the validation and return! – Zico Dec 21 '17 at 09:21
  • @Zico Sorry i did not quite understood that. Can you make a working example as an answer? – J.Doo Dec 21 '17 at 09:24

1 Answers1

0

use below code in your controller.

@RequestMapping(value = "/user/register", method = RequestMethod.POST)
    public SecurityToken register(@RequestBody @Valid SecurityUser user, BindingResult result) {
        List<ObjectError> st = result.getAllErrors();
        String errorFields = "";
        for (ObjectError error : st) {
            errorFields = errorFields + error.getDefaultMessage();
        }
        if (!errorFields.equals("")) {
            throw new CustomException(errorFields);
        }
        return service.register(user);
    }

you can use annotation for fields as :

import org.hibernate.validator.constraints.NotEmpty;

public class SecurityUser {

    @NotEmpty(message = "Email id is mandatory.")
    private String emailId;

For Exception handling you can use HandlerMethod in spring controller or ControllerAdvice.

@ControllerAdvice
public class SecurityControllerAdvice {

    @ExceptionHandler(CustomException.class)
    @ResponseBody
    public CustomResponse handleSecurityException(CustomException se) {
        CustomResponse response = new CustomResponse(se.getMessage());
        return response;
    }
}
Neeraj Benjwal
  • 1,271
  • 10
  • 10
  • I do not think this will work cause `HttpMessageNotReadableException` can be thrown for any of the variables. How can i know which one them threw it so to return the right message. Example: `password cannot be null` or `username cannot be null` ? – J.Doo Dec 21 '17 at 09:22
  • you can use binding result for this purpose where you can get the fields error @RequestMapping(value = "/user/register", method = RequestMethod.POST) public SecurityToken register(@RequestBody SecurityUser user, BindingResult result) { //you can use below methods for your check and then can throw custom exception which can be handled by the AdviceController result.hasFieldErrors(); result.hasErrors(); result.getAllErrors(); result.hasErrors(); return service.register(user); } – Neeraj Benjwal Dec 21 '17 at 09:31
  • for custom exception handling you can see below link : https://stackoverflow.com/questions/47899292/how-to-throw-an-exception-back-in-json-in-spring-boot/47918918#47918918 – Neeraj Benjwal Dec 21 '17 at 09:33
  • Can you implement it as an example in your post? I am not quite sure i understood your mechanic – J.Doo Dec 21 '17 at 09:49
  • @J.Doo I have modified my answer as per your requirement, please have a look. – Neeraj Benjwal Dec 21 '17 at 10:26
  • How can you know in `register` controller which exception to throw? – J.Doo Dec 21 '17 at 11:11
  • I am using global exception handling and using custom exceptions, it's not controller specific, you can refer the link to create, throw and handle the custom exception. https://stackoverflow.com/questions/47899292/how-to-throw-an-exception-back-in-json-in-spring-boot/47918918#47918918 – Neeraj Benjwal Dec 21 '17 at 13:08