Home > OS >  Spring Boot Rest API handling unique constraint
Spring Boot Rest API handling unique constraint

Time:12-04

I have a Spring Boot Rest API. I want to create users and set a unique constraint on their email and username. That works well so far. Here are the main classes and methods:

@Entity
@NoArgsConstructor
@Getter
@Setter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(unique = true)
    @NotNull
    private String email;
    @Column(unique = true)
    @NotNull
    private String username;
    @NotNull
    private String password;

    public User(String email, String username, String password) {
        this.email = email;
        this.password = password;
        this.username = username;
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SignupRequest {
    @NotNull
    private String email;
    @NotNull
    private String username;
    @NotNull
    private String password;
}

@CrossOrigin(value = "*")
@PostMapping("/signup")
public ResponseEntity<?> signup(@Valid @RequestBody SignupRequest signupRequest) {
    signupService.signup(signupRequest);
    return ResponseEntity.ok().build();
}

@Service
public class SignupServiceImpl implements SignupService {

    @Override
    public void signup(SignupRequest signupRequest) throws MessagingException, UnsupportedEncodingException {
        User user = new User();
        User user = new User(signupRequest.getEmail(), signupRequest.getUsername(), signupRequest.getPassword());
        user = userRepository.save(user);
    }
}

@Repository
@Component
public interface UserRepository extends CrudRepository<User, Long> {}

Now, the thing is, when I send a POST request to that endpoint with a username or email that already exists, I just get the http response 500 Internal Server Error. But I want to return a different status code and some Error message indicating that the email/username already exists.

Now two questions:

  1. How can I modify the response globally? I could surround the userRepository.save(user) method with a try catch block, but I would have to do that in all the methods where I save a user separately. Can I define something like that globally?
  2. The userRepository.save(user) method just returns a. JdbcSQLIntegrityConstraintViolationException with a pretty verbose message. Is there a way to clearly determine WHAT exactly went wrong (unique username constraint failed, unique email constraint failed, ...)? I could check if a user with that username or email exists by writing a method in the userRepository, but that looks like a lot of unnecessary sql queries to me. Is there a better way?

CodePudding user response:

To answer your first question, You can handle exception globally via spring exception handling mechanism. You could use spring ControllerAdvice. Here you can set generic error response and custom http code. Here is an example of ControllerAdvice

@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler 
{
    @ExceptionHandler(UserNotFoundException.class)
    public final ResponseEntity<ErrorResponse> handleUserNotFoundException(UserNotFoundException ex, WebRequest request) {
        String details = ex.getLocalizedMessage();
        ErrorResponse error = new ErrorResponse(ApplicationConstants.RECORD_NOT_FOUND, details);
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(Exception.class)
    public final ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex, WebRequest request) {
        String details = ex.getLocalizedMessage();
        ErrorResponse error = new ErrorResponse(ApplicationConstants.SERVER_ERROR, details);
        return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

public class ErrorResponse
{
    public ErrorResponse(String message, String details) {
        super();
        this.message = message;
        this.details = details;
    }

    private String message;
    private String details;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getDetails() {
        return details;
    }

    public void setDetails(String details) {
        this.details = details;
    }
}

Now about second question you can loop through all the cause and check unique constraint name to find out what exception violated. But better approach would be to check first and if found then throw error.

  • Related