Saturday 24 June 2023

Exception Handling and Validation in Spring Boot

Handling exceptions is an important part of building a robust application. This tutorial I will illustrate how to implement Exception Handling and Validation with Spring for a REST API
please follow the steps and download the code below. 



-------------------------------- UserController -------------------------------------------

package com.ramsis.restApi.controller;

import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ramsis.restApi.payloads.ApiResponse;
import com.ramsis.restApi.payloads.UserDto;
import com.ramsis.restApi.services.UserService;

@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;

@PostMapping("/")
public ResponseEntity<UserDto>addUser(@Valid @RequestBody UserDto userDto)
{
UserDto createUser=this.userService.createuser(userDto);
return new ResponseEntity<>(createUser,HttpStatus.CREATED);
}

@PutMapping("/{userId}")
public ResponseEntity<UserDto> updateUser(@Valid @RequestBody UserDto userDto , @PathVariable("userId")  Integer userId){

UserDto updateUser=this.userService.updateUser(userDto, userId);
return ResponseEntity.ok(updateUser);
}

@DeleteMapping("/{userId}")
public ResponseEntity<ApiResponse>deletedUser(@PathVariable("userId") Integer userId)
{
this.userService.deleteUser(userId);
return new ResponseEntity<ApiResponse>(new ApiResponse("user delete succsfully",String.valueOf(HttpStatus.OK.value()),true),HttpStatus.OK);

}
@GetMapping("/")
public ResponseEntity<List<UserDto>> getAllUsers()
{
return ResponseEntity.ok(this.userService.getAllUsers());
}

@GetMapping("/{userId}")
public ResponseEntity<UserDto> getAllUsers(@PathVariable("userId") Integer userId)
{
return ResponseEntity.ok(this.userService.getByuserId(userId));
}

}







--------------------------------- GlobelExceptionHandler --------------------------------

package com.ramsis.restApi.exception;

import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.ramsis.restApi.payloads.ApiResponse;

@RestControllerAdvice
public class GlobelExceptionHandler {
@ExceptionHandler(ResourcesNotoundExcpetion.class)
public ResponseEntity<ApiResponse> resourceNotfoiunexceptionHandler(ResourcesNotoundExcpetion ex)
{
String message=ex.getMessage();
ApiResponse apiResponse=new ApiResponse(message,String.valueOf(HttpStatus.NOT_FOUND.value()),false);
return new ResponseEntity<ApiResponse>(apiResponse,HttpStatus.NOT_FOUND);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleMethodArgsNotValidateExpection(MethodArgumentNotValidException ex)
{
Map<String, String> resp=new HashMap<>();
ex.getBindingResult().getAllErrors().forEach(error->{
String filedName=((FieldError)error).getField();
String messsage=error.getDefaultMessage();
resp.put(filedName, messsage);
});
return new ResponseEntity<Map<String,String>>(resp,HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(ApiException.class)
public ResponseEntity<ApiResponse> handleApiException(ApiException ex)
{
String message=ex.getMessage();
ApiResponse apiResponse=new ApiResponse(message,String.valueOf(HttpStatus.BAD_REQUEST.value()),true);
return new ResponseEntity<ApiResponse>(apiResponse,HttpStatus.BAD_REQUEST);
}

}

----------------------------------- ResourcesNotoundExcpetion -----------------------------------

package com.ramsis.restApi.exception;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ResourcesNotoundExcpetion  extends RuntimeException{

String resourceName;
String feildName;
long feildValue;
public ResourcesNotoundExcpetion(String resourceName, String feildName, long feildValue) {
super(String.format("%s not found with his name %s :%s", resourceName,feildName,feildValue));
this.resourceName = resourceName;
this.feildName = feildName;
this.feildValue = feildValue;
}
}

------------------------ ApiResponse--------------------------

package com.ramsis.restApi.payloads;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse {
private String  meaassge;
private String  status;
private boolean success;

}

--------------------------- userDto --------------------------------

package com.ramsis.restApi.payloads;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@Setter
@Getter
public class UserDto {
private int userId;
@NotEmpty
@Size(min = 4,message ="username must be minimum 4 character")
private String name;
@Email(message = "your email id wrong")
private String email;
@NotEmpty
//@Size(min = 3,max = 15, message = "password must be minimum 3 and max 12 character")
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#&()–[{}]:;',?/*~$^+=<>]).{5,15}$", message = ""
+ "Password must contain at least one digit [0-9]."
+ "Password must contain at least one lowercase Latin character [a-z]."
+ "Password must contain at least one uppercase Latin character [A-Z]."
+ "Password must contain at least one special character like ! @ # & ( )."
+ "Password must contain a length of at least 5 characters and a maximum of 15 characters.")
private String password;
private String region;
@Size(min = 2,message ="department must be minimum 2 character")
private String department;


}

--------------------------------- userRepository ------------------------------------

package com.ramsis.restApi.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.ramsis.restApi.entities.User;

public interface UserRepository  extends JpaRepository<User, Integer>{
  Optional<User> findByEmail(String email);
}

---------------------------- userService --------------------------

package com.ramsis.restApi.services;

import java.util.List;

import com.ramsis.restApi.payloads.UserDto;

public interface UserService {
UserDto registerNewUser(UserDto user);
UserDto createuser(UserDto user);
UserDto updateUser(UserDto user, Integer userId);
UserDto getByuserId(Integer userId);
List<UserDto> getAllUsers();
void deleteUser(Integer userId);

}

--------------------------- userServiceImpl --------------------

package com.ramsis.restApi.services;

import java.util.List;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ramsis.restApi.entities.User;
import com.ramsis.restApi.exception.ResourcesNotoundExcpetion;
import com.ramsis.restApi.payloads.UserDto;
import com.ramsis.restApi.repository.UserRepository;

@Service
public class UserServiceImpl  implements UserService{
@Autowired
private ModelMapper mapper;
@Autowired
private UserRepository userRepository;

@Override
public UserDto createuser(UserDto userDto) {
User user=this.dtoToUser(userDto);
User saveUsers=this.userRepository.save(user);
return this.userToDto(saveUsers);
}

@Override
public UserDto updateUser(UserDto userDto, Integer userId) {
User user=this.userRepository.findById(userId)
.orElseThrow(()-> new ResourcesNotoundExcpetion("User","Id",userId));
user.setName(userDto.getName());
user.setEmail(userDto.getEmail());
user.setPassword(userDto.getPassword());
user.setRegion(userDto.getRegion());
user.setDepartment(userDto.getDepartment());
User updateUser=this.userRepository.save(user);
UserDto userDto1= this.userToDto(updateUser);
return userDto1;
}

@Override
public UserDto getByuserId(Integer userId) {
User user=this.userRepository.findById(userId)
.orElseThrow(()-> new ResourcesNotoundExcpetion("User","Id",userId));
return this.userToDto(user);
}

@Override
public List<UserDto> getAllUsers() {

List<User> users=this.userRepository.findAll();
List<UserDto> userDtos=users.stream().map(user->this.userToDto(user)).collect(Collectors.toList());
return userDtos;
}

@Override
public void deleteUser(Integer userId) {
User user =this.userRepository.findById(userId).orElseThrow(()-> new ResourcesNotoundExcpetion("User","Id",userId));
this.userRepository.delete(user);
}
@Override
public UserDto registerNewUser(UserDto userDto) {
User user = this.mapper.map(userDto, User.class);
//endoed the password
user.setPassword(user.getPassword());
User newUser=this.userRepository.save(user);
return this.mapper.map(newUser, UserDto.class);
}

private User dtoToUser(UserDto userDto)
{
User user=this.mapper.map(userDto, User.class);
// user.setUserId(userDto.getUserId());
// user.setName(userDto.getName());
// user.setEmail(userDto.getEmail());
// user.setDepartment(userDto.getDepartment());
// user.setPassword(userDto.getPassword());
// user.setRegion(userDto.getRegion());
return user;
}
public UserDto userToDto (User user)
{
UserDto userDto=this.mapper.map(user, UserDto.class);
// userDto.setUserId(user.getUserId());
// userDto.setName(user.getName());
// userDto.setEmail(user.getEmail());
// userDto.setDepartment(user.getDepartment());
// userDto.setPassword(user.getPassword());
// userDto.setRegion(user.getRegion());
return userDto;
}

}

------------------------- pom.xml ----------------------------

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ramsis</groupId>
<artifactId>restApiWithValidationErrorHandling</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>restApiWithValidationErrorHandling</name>
<description>Demo project for Spring boot Exception Handling and validation</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>


------------------------------ application.properties -------------------------

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=cctns@123

spring.jpa.show-sql = true
spring.jpa.properties.hibernate.format_sql=true

logging.level.org.hibernate.type.descriptor.sql=trace

spring.jpa.hibernate.ddl-auto = update
#spring.jpa.hibernate.ddl-auto = none





No comments:

Post a Comment