Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error validation message format Spring boot

i am new using Java and Spring boot, im trying to validate a json input that will be stored in DB, the validation is doing well, the problem is the response format, i think it is not readable for a client as Angular JS, in need to know how to change the output format from this :

{
"error": {
    "code": 400,
    "message": "Validation failed for classes [com.company.customerhelpcenter.domain.Ticket] during persist time for groups [javax.validation.groups.Default, ]\nList of constraint violations:[\n\tConstraintViolationImpl{interpolatedMessage='Descripción no puede ser null', propertyPath=description, rootBeanClass=class com.company.customerhelpcenter.domain.Ticket, messageTemplate='Descripción no puede ser null'}\n\tConstraintViolationImpl{interpolatedMessage='not a well-formed email address', propertyPath=email, rootBeanClass=class com.company.customerhelpcenter.domain.Ticket, messageTemplate='{org.hibernate.validator.constraints.Email.message}'}\n\tConstraintViolationImpl{interpolatedMessage='Número de teléfono no puede ser null', propertyPath=phone, rootBeanClass=class com.company.customerhelpcenter.domain.Ticket, messageTemplate='Número de teléfono no puede ser null'}\n]"
}}

to this

{
"error": {
    "description" : "Description cannot be null",
    "email": "Email cannot be null",
    "phone": "Phone cannot be null"
} }

This is my controller

@PostMapping
public ResponseEntity<?> create(@RequestBody Ticket ticket)
{
    return new ResponseEntity<>(this.ticketService.create(ticket), HttpStatus.CREATED);
}

Service:

public Ticket create(Ticket ticket)
{
    return this.ticketRepository.save(ticket);
}

Repository:

@Repository
public interface TicketRepository extends JpaRepository<Ticket, Integer>, Serializable
{}

Entity

@Entity
@Table(name = "ticket")
public class Ticket implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

    @Basic(optional = false)
    @NotNull(message = "Descripción no puede ser null")
    @Size(min = 1, max = 255)
    @Column(name = "description")
    private String description;

    @Basic(optional = false)
    @NotNull(message = "Número de teléfono no puede ser null")
    @Size(min = 1, max = 20)
    @Column(name = "phone")
    private String phone;

    @Basic(optional = false)
    @NotNull(message = "Email no puede ser null")
    @Size(min = 1, max = 80)
    @Email
    @Column(name = "email")
    private String email;

    @Basic(optional = false)
    @NotNull
    @Column(name = "locked")
    private boolean locked;

    @JoinColumn(name = "ticket_type_id", referencedColumnName = "id")
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    @Exclude
    private TicketType ticketType;

    @OneToMany(mappedBy = "ticket")
    private Set<TicketHistory> ticketHistories = new HashSet<TicketHistory>();


    public Integer getId()
    {
        return this.id;
    }

    public String getDescription()
    {
        return this.description;
    }

    public String getPhone()
    {
        return this.phone;
    }

    public String getEmail()
    {
        return this.email;
    }

    public TicketType getTicketType()
    {
        return this.ticketType;
    }

    public boolean isLocked()
    {
        return this.locked;
    }

    public Set<TicketHistory> getTicketHistories()
    {
        return this.ticketHistories;
    }
}
like image 251
Andres Felipe S Avatar asked Jan 29 '23 22:01

Andres Felipe S


1 Answers

Your exception is a ConstraintValidationException, it means that the code passed through the Controller but couldn't save the data on database due to validation. My recommendation is that you validate the data before that, validate your data before it gets the Controller. So, would be like this:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResponseEntity<Map<String, Object>> yourExceptionHandler(MethodArgumentNotValidException e) {
        Map<String, Object> response = new HashMap<String, Object>();
        Map<String, String> errors = new HashMap<String, String>();

        BindingResult bindingResult = e.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            errors.put(fieldError.getField(), fieldError.getDefaultMessage());
        }

        response.put("error", errors);
        return new ResponseEntity<Map<String, Object>>(response, HttpStatus.BAD_REQUEST);
    }
}

And your controller should have this @Valid annotation:

@PostMapping
public ResponseEntity<?> create(@RequestBody @Valid Ticket ticket) {
    return new ResponseEntity<>(this.ticketService.create(ticket), HttpStatus.CREATED);
}

Then, your validations on Ticket like @NotNull and @Size and etc. will be validated.

BUT, if you don't want to validate like this and you want only to deal with ConstraintValidationException, you can do this:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler({ ConstraintViolationException.class})
    public ResponseEntity<Map<String, Object>> yourExceptionHandler(ConstraintViolationException e) {
        Map<String, Object> response = new HashMap<String, Object>();
        Map<String, String> errors = new HashMap<String, String>();
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();

        for (ConstraintViolation<?> constraintViolation : constraintViolations) {
            errors.put(constraintViolation.getPropertyPath().toString() , constraintViolation.getMessage());
        }

        response.put("error", errors);
        return new ResponseEntity<Map<String, Object>>(response, HttpStatus.BAD_REQUEST);
    }
}
like image 168
Raphael Amoedo Avatar answered Feb 02 '23 12:02

Raphael Amoedo