Pretty message on exceptions from REST app - java

I have a problem with my REST app. Here is the git link to ease your burden: https://github.com/TheNiceGuy123/AstonishingBackEnd
Here is the raw code also:
Classes posted in order:
Employee Controller:
package com.example.csmartbackend.controller;
import com.example.csmartbackend.model.Employee;
import com.example.csmartbackend.modeldto.EmployeeDto;
import com.example.csmartbackend.service.EmployeeService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import java.util.UUID;
#RestController
#RequestMapping("employee")
#RequiredArgsConstructor
public class EmployeeController
{
private final EmployeeService employeeService;
#GetMapping("find/{Cnp}")
public ResponseEntity<Employee> findByCNP(#PathVariable("Cnp") String Cnp)
{
Employee employee = employeeService.findByCnp(Cnp);
HttpHeaders header = new HttpHeaders();
header.add("Desc","Getting employee by id.");
return ResponseEntity.status(HttpStatus.OK).headers(header).body(employee);
}
}
Employee Service:
package com.example.csmartbackend.service;
import com.example.csmartbackend.mapper.EmployeeMapper;
import com.example.csmartbackend.model.Employee;
import com.example.csmartbackend.modeldto.EmployeeDto;
import com.example.csmartbackend.repository.EmployeeRepository;
import exception.general.TargetNotFoundException;
import exception.requestsexceptions.CNPNotFoundException;
import exception.requestsexceptions.EmployeeNotFoundException;
import exception.requestsexceptions.InvalidIdException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
#Service
#RequiredArgsConstructor
#Transactional
public class EmployeeService implements EmployeeServiceImpl
{
private final EmployeeRepository employeeRepository;
private final ContractService contractService;
private final AdressService adressService;
public Employee findByCnp(String Cnp) throws CNPNotFoundException
{
Optional<Employee> employeeOptional = Optional.ofNullable(employeeRepository.findByCnp(Cnp));
if(employeeOptional.isPresent())
return employeeOptional.get();
else
throw new CNPNotFoundException("No employee found.");
}
}
Global Exception Handler:
package exception.requestsexceptions;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler
{
#Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported
(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request)
{
String message = ex.getMessage();
List<String> details = new ArrayList<>();
details.add("Request method not supported.");
ApiErrorModel apiErrorModel = new ApiErrorModel(message, details, status, LocalDateTime.now());
return ResponseEntity.status(status).body(apiErrorModel);
}
#Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported
(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request)
{
String message = ex.getMessage();
List<String> details = new ArrayList<>();
details.add("Media method not supported.");
ApiErrorModel apiErrorModel = new ApiErrorModel(message, details, status, LocalDateTime.now());
return ResponseEntity.status(status).body(apiErrorModel);
}
#Override
protected ResponseEntity<Object> handleMissingPathVariable
(MissingPathVariableException ex, HttpHeaders headers, HttpStatus status, WebRequest request)
{
String message = ex.getMessage();
List<String> details = new ArrayList<>();
details.add("Path variable is missing.");
ApiErrorModel apiErrorModel = new ApiErrorModel(message, details, status, LocalDateTime.now());
return ResponseEntity.status(status).body(apiErrorModel);
}
#Override
protected ResponseEntity<Object> handleMissingServletRequestParameter
(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request)
{
String message = ex.getMessage();
List<String> details = new ArrayList<>();
details.add("Request parameter is missing.");
ApiErrorModel apiErrorModel = new ApiErrorModel(message, details, status, LocalDateTime.now());
return ResponseEntity.status(status).body(apiErrorModel);
}
#Override
protected ResponseEntity<Object> handleTypeMismatch
(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request)
{
String message = ex.getMessage();
List<String> details = new ArrayList<>();
details.add("Mismatch of type.");
ApiErrorModel apiErrorModel = new ApiErrorModel(message, details, status, LocalDateTime.now());
return ResponseEntity.status(status).body(apiErrorModel);
}
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable
(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request)
{
String message = ex.getMessage();
List<String> details = new ArrayList<>();
details.add("Request body is not readable.");
ApiErrorModel apiErrorModel = new ApiErrorModel(message, details, status, LocalDateTime.now());
return ResponseEntity.status(status).body(apiErrorModel);
}
#ExceptionHandler(EmployeeNotFoundException.class)
public ResponseEntity<Object> handleEmployeeNotFoundException(EmployeeNotFoundException ex)
{
String message = ex.getMessage();
List<String> details = new ArrayList<>();
details.add("Employee not found.");
ApiErrorModel apiErrorModel = new ApiErrorModel(message, details, HttpStatus.BAD_REQUEST, LocalDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(apiErrorModel);
}
#ExceptionHandler(CNPNotFoundException.class)
public ResponseEntity<Object> handleInvalidCNPException(CNPNotFoundException ex)
{
String message = ex.getMessage();
List<String> details = new ArrayList<>();
details.add("Employee with the given CNP not found.");
ApiErrorModel apiErrorModel = new ApiErrorModel(message, details, HttpStatus.NOT_FOUND, LocalDateTime.now());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(apiErrorModel);
}
}
CNPNotFoundException class:
package exception.requestsexceptions;
public class CNPNotFoundException extends RuntimeException
{
public CNPNotFoundException(String message) { super(message); }
}
ApiErrorModel class:
package exception.requestsexceptions;
import lombok.*;
import org.springframework.http.HttpStatus;
import java.time.LocalDateTime;
import java.util.List;
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class ApiErrorModel
{
String message;
List<String> details;
HttpStatus status;
LocalDateTime timestamp;
}
I want to give some relevant informations to the user like in the second picture but in postman everything remains at the default level to call it like that, like my code from the handler is not even there. Is any logic or annotation that I miss?
This is how it looks on my side
This is how it should look like
Thank you for your time!

Problem is when a class is annotated with the #SpringBootApplication without any attribute SpringBoot only scans the package and its subpackages where the class itself is located.
In your case it means SpringBoot only looking for any Spring components under com.example.csmartbackend package while you put GlobalExceptionHandler class in exception.requestsexceptions package.
To fix it you have two choices:
put the exception package under com.example.csmartbackend, so it would be com.example.csmartbackend.exception.requestsexception.GlobalExceptionHandler
tell spring to scan the exception package as well, either by using scanBasePackageClasses or scanBasePackages attribute, e.g #SpringBootApplication(scanBasePackageClasses = {CSmartBackEndApplication.class, GlobalExceptionHandler.class})

Related

spring boot mfa totp code verification on login not working

I am trying to implement mfa authentication in my app using totp. Bellow is the library i use. All is going well for registering the user, i receive the qr code, scan it and get every 30 secs the code in google authenticator. When i am trying to login to verify the code, the code verification doesnt work (in auth service, method Verify). I've spent several hours but cant figure it out, tried different users, logs but without success.
<dependency>
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp</artifactId>
<version>1.7.1</version>
</dependency>
this is my code
AuthContoller.java
import com.example.jsonfaker.model.dto.LoginRequest;
import com.example.jsonfaker.model.dto.SignupRequest;
import com.example.jsonfaker.model.dto.VerifyRequest;
import com.example.jsonfaker.service.Exporter;
import com.example.jsonfaker.service.UserAuthService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
#RestController
#RequestMapping("/auth")
#CrossOrigin
public class AuthController {
private final Exporter exporter;
private final UserAuthService userAuthService;
public AuthController(Exporter exporter, UserAuthService userAuthService) {
this.exporter = exporter;
this.userAuthService = userAuthService;
}
#PostMapping("/login")
public ResponseEntity<String> authenticateUser(#Valid #RequestBody LoginRequest loginRequest) {
String response = userAuthService.login(loginRequest);
return ResponseEntity
.ok()
.body(response);
}
#PostMapping("/register2FA")
public ResponseEntity<byte[]> registerUser2FA(#Valid #RequestBody SignupRequest signupRequest) throws Exception {
userAuthService.register2FA(signupRequest);
byte[] qrCodeBytes = userAuthService.mfaAccountSetup(signupRequest.getUsername());
return ResponseEntity
.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "inline;filename=\""+exporter.exportFileNameQR() + ".png\"")
.body(qrCodeBytes);
}
#PostMapping("/register")
public ResponseEntity<?> registerUser(#Valid #RequestBody SignupRequest signupRequest) throws Exception {
userAuthService.simpleRegister(signupRequest);
return ResponseEntity.ok(HttpStatus.CREATED);
}
#PostMapping("/verify")
public ResponseEntity<String> authenticateUser2FA(#Valid #RequestBody VerifyRequest verifyRequest) throws Exception {
String response = userAuthService.verify(verifyRequest.getUsername(), verifyRequest.getCode());
return ResponseEntity
.ok()
.body(response);
}
}
this is my token manager
import dev.samstevens.totp.code.*;
import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import dev.samstevens.totp.time.SystemTimeProvider;
import dev.samstevens.totp.time.TimeProvider;
import dev.samstevens.totp.util.Utils;
import org.springframework.stereotype.Service;
#Service("mfaTokenManager")
public class DefaultMFATokenManager implements MFATokenManager {
private final SecretGenerator secretGenerator;
private final QrGenerator qrGenerator;
private final CodeVerifier codeVerifier;
public DefaultMFATokenManager(SecretGenerator secretGenerator, QrGenerator qrGenerator, CodeVerifier codeVerifier) {
this.secretGenerator = secretGenerator;
this.qrGenerator = qrGenerator;
this.codeVerifier = codeVerifier;
}
#Override
public String generateSecretKey() {
return secretGenerator.generate();
}
#Override
public String getQRCode(String secret) throws QrGenerationException {
QrData data = new QrData.Builder().label("MFA")
.secret(secret)
.issuer("Daniel token")
.algorithm(HashingAlgorithm.SHA1)
.digits(6)
.period(30)
.build();
return Utils.getDataUriForImage(
qrGenerator.generate(data),
qrGenerator.getImageMimeType()
);
}
#Override
public boolean verifyTotp(String code, String secret) {
TimeProvider timeProvider = new SystemTimeProvider();
CodeGenerator codeGenerator = new DefaultCodeGenerator();
CodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
System.out.println(timeProvider.getTime());
System.out.println(codeGenerator);
return verifier.isValidCode(secret, code);
}
}
this is my auth service
import com.example.jsonfaker.model.Roles;
import com.example.jsonfaker.model.SystemUser;
import com.example.jsonfaker.model.dto.LoginRequest;
import com.example.jsonfaker.model.dto.SignupRequest;
import com.example.jsonfaker.model.dto.TokenResponse;
import com.example.jsonfaker.repository.RolesRepository;
import com.example.jsonfaker.repository.SystemUserRepository;
import com.example.jsonfaker.security.AuthoritiesConstants;
import com.example.jsonfaker.security.jwt.JwtUtils;
import com.example.jsonfaker.twoFA.MFATokenManager;
import com.example.jsonfaker.twoFA.MfaTokenData;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.stream.Collectors;
import static java.util.Objects.nonNull;
#Service
public class UserAuthService {
private final SystemUserRepository systemUserRepository;
private final RolesRepository rolesRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final MFATokenManager mfaTokenManager;
private final AuthenticationManager authenticationManager;
private final LoginUserService loginUserService;
private final JwtUtils jwtUtils;
public UserAuthService(SystemUserRepository systemUserRepository, RolesRepository rolesRepository, BCryptPasswordEncoder bCryptPasswordEncoder, MFATokenManager mfaTokenManager, AuthenticationManager authenticationManager, LoginUserService loginUserService, JwtUtils jwtUtils) {
this.systemUserRepository = systemUserRepository;
this.rolesRepository = rolesRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.mfaTokenManager = mfaTokenManager;
this.authenticationManager = authenticationManager;
this.loginUserService = loginUserService;
this.jwtUtils = jwtUtils;
}
public void simpleRegister(SignupRequest signupRequest) throws Exception {
if(systemUserRepository.findByUsername(signupRequest.getUsername()).isPresent()){
throw new Exception("User with this username exists");
}
Roles simpleUserRole = new Roles();
simpleUserRole.setName(AuthoritiesConstants.USER);
SystemUser user = new SystemUser();
user.setPassword(bCryptPasswordEncoder.encode(signupRequest.getPassword()));
user.setUsername(signupRequest.getUsername());
user.setAuthorities(rolesRepository.findAllByName("ROLE_USER").stream().collect(Collectors.toSet()));
user.setSecret(mfaTokenManager.generateSecretKey());
systemUserRepository.save(user);
}
public void register2FA(SignupRequest signupRequest) throws Exception {
if(systemUserRepository.findByUsername(signupRequest.getUsername()).isPresent()){
throw new Exception("User with this username exists");
}
Roles simpleUserRole = new Roles();
simpleUserRole.setName(AuthoritiesConstants.USER);
SystemUser user = new SystemUser();
user.setPassword(bCryptPasswordEncoder.encode(signupRequest.getPassword()));
user.setUsername(signupRequest.getUsername());
user.setAuthorities(rolesRepository.findAllByName("ROLE_USER").stream().collect(Collectors.toSet()));
user.setTwoFAisEnabled(Boolean.TRUE);
user.setSecret(mfaTokenManager.generateSecretKey());
systemUserRepository.save(user);
}
public byte[] mfaAccountSetup(String username) throws Exception {
SystemUser user = systemUserRepository.findByUsername(username).get();
if (!nonNull(user)){
throw new Exception("Unable to find user with this username");
}
if(!user.isTwoFAisEnabled()){
throw new Exception("2FA is not enabled for this account");
}
MfaTokenData token = new MfaTokenData(mfaTokenManager.getQRCode(user.getSecret()), user.getSecret());
System.out.println("Mfa code :" +token.getMfaCode());
String base64Image = token.getQrCode().split(",")[1];
byte[] imageBytes = javax.xml.bind.DatatypeConverter.parseBase64Binary(base64Image);
return imageBytes;
}
public String login(LoginRequest loginRequest){
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
if(systemUserRepository.findByUsername(loginRequest.getUsername()).get().isTwoFAisEnabled()){
return "verify code now";
}
SystemUser userDetails = (SystemUser) authentication.getPrincipal();
String jwt = jwtUtils.generateJwtToken(userDetails);
return new TokenResponse(jwt).toString();
}
public String verify(String username, String code) throws Exception {
SystemUser user = systemUserRepository.findByUsername(username).get();
if (!nonNull(user)){
throw new Exception("Unable to find user with this username");
}
if (!mfaTokenManager.verifyTotp(code, user.getSecret())){
return "unable to auth";
}
return "token here";
}
}
Finally found the problem, on my phone the time was delayed by 2 minutes, I've set it to be the same as on my computer and worked. The problem is that when validating the token the app uses an interval of 30 seconds for each token generation, and if the delay on the phone or other device is bigger than 30 sec in future or past the timestamp doesnt match the one used for verification.
Here is the documentation for the library i used, make sure to read it carefully before using it.
https://github.com/samdjstevens/java-totp
Here is the article which i followed in my project:
https://www.javadevjournal.com/spring-security/two-factor-authentication-with-spring-security/
Useful reading before starting a project using TOTP:
https://www.freecodecamp.org/news/how-time-based-one-time-passwords-work-and-why-you-should-use-them-in-your-app-fdd2b9ed43c3/
A youtube video about 2FA:
https://www.youtube.com/watch?v=ZXFYT-BG2So

ControllerAdvice not catching exception

I am learning java and using spring boot to develop web app. I am using #controllerAdvice to catch all the exceptions but none of my exceptions is being intercepted by it. I am using spring boot 2.6.2. git repo https://github.com/jitenderchand1/java-microservices
Controller file
package com.learning.rest.webservices.restfulwebservices.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#RequestMapping("/user-service")
public class UserController {
#Autowired
private UserDaoService service;
#GetMapping(path = "/users")
public List<User> retrieveAllUser() {
return service.getAllUser();
}
#GetMapping(path = "/users/{id}")
public User retrieveOneUser(#PathVariable Integer id) {
User user = service.fineOne(id);
if(user == null){
throw new UserNotFoundException("id -" + id);
//throw new Exception();
}
return user;
}
#PostMapping(path = "/user/save")
public ResponseEntity<User> saveUser(#RequestBody User user) {
User savedUser = service.save(user);
return new ResponseEntity(savedUser, HttpStatus.CREATED);
}
}
custom Exception handler
package com.learning.rest.webservices.restfulwebservices.user;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.Date;
#ControllerAdvice
#RestController
public class CustomException extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), "I messed up", request.getDescription(true));
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(UserNotFoundException.class)
public final ResponseEntity<Object> handleUserNotFoundException(UserNotFoundException ex, WebRequest request) {
ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), "You are not from this world",
request.getDescription(false));
return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND);
}
}

Sprint boot custom exception not getting caught and response is always empty if error with status 200

I'm struggling a lot with spring-boot custom exception handling. Customer exception is not getting caught with exception handler. REST API works fine for valid request payload. I'm trying to send error response when there's an error. But error response is always empty with status code 200 instead of 404.
Controller
package com.company.paypage.v2.controller;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.company.paypage.exception.*;
import com.company.paypage.model.ErrorMessageConstants;
import com.company.paypage.v2.model.ConfigPayload;
import com.company.paypage.v2.model.ConfigResponse;
import com.company.paypage.v2.services.FeatureConfigService;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#RestController
#RequestMapping(value = "v2/setup")
public class FeatureConfigController {
#Autowired
private FeatureConfigService featureconfigService;
/*
features config endpoint
*/
#RequestMapping(value = "/config", method = POST, consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ConfigResponse setupConfigRequest(#Valid #RequestBody ConfigPayload payload, HttpServletRequest request, HttpServletResponse servResponse) {
log.info("Processing the feature config request for " + payload.getPage_type());
ConfigResponse response = null;
try {
response = featureconfigService.processConfigRequest(payload);
System.out.println(response);
if(response == null) {
throw new FeatureConfigException(ErrorMessageConstants.NOT_FOUND, "Error while generating feature config response.....");
}
} catch (FeatureConfigException e){
log.error("Exception:", e);
}
return response;
}
}
Exception class
package com.company.paypage.exception;
public class FeatureConfigException extends Exception {
String code;
String message;
public FeatureConfigException(String code, String message) {
super(message);
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
#Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Exception handler
package com.company.paypage.exception;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;
import com.company.paypage.model.ApplicationConstants;
import com.company.paypage.model.ErrorCodeConstants;
import com.company.paypage.model.ErrorMessageConstants;
import com.company.paypage.model.GeneralErrorInfo;
import com.company.paypage.model.Payment;
import com.company.paypage.model.SetupResponse;
import com.company.paypage.v2.model.ConfigResponse;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#ControllerAdvice
public class GeneralExceptionHandler{
#ExceptionHandler(FeatureConfigException.class)
protected ResponseEntity<ConfigResponse> handleFeatureConfigException(FeatureConfigException ex, HttpServletRequest request){
GeneralErrorInfo generalErrorInfo = new GeneralErrorInfo().withCode(ex.getCode());
generalErrorInfo.setMessage(ex.getMessage());
String referenceId =(String) request.getAttribute(ApplicationConstants.REFERENCE_ID);
ConfigResponse configResponse = buildConfigResponse(generalErrorInfo, referenceId);
log.error("{} {}-{}"
, ex.getMessage()
, request.getHeader(ApplicationConstants.X_GP_REQUEST_ID)
, referenceId
, ex);
return new ResponseEntity<ConfigResponse>(configResponse, addCustomerHeaders(request), HttpStatus.BAD_REQUEST);
}
#SuppressWarnings("null")
ConfigResponse buildConfigResponse(GeneralErrorInfo generalErrorInfo, String referenceId) {
ConfigResponse configResponse = new ConfigResponse();
configResponse.setError(generalErrorInfo);
configResponse.setAutocomplete((Boolean) null);
configResponse.setRefund((Boolean) null);
configResponse.setSplit_payment((Boolean) null);
configResponse.setTender_type(null);
configResponse.setVoidd((Boolean) null);
return configResponse;
}
}
ConfigResponse model
package com.company.paypage.v2.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.company.paypage.model.GeneralErrorInfo;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"tender_type",
"autocomplete",
"split_payment",
"refund",
"void",
"error"
})
public class ConfigResponse implements Serializable {
#JsonProperty("tender_type")
private TenderType tender_type;
#JsonProperty("autocomplete")
private boolean autocomplete;
#JsonProperty("split_payment")
private boolean split_payment;
#JsonProperty("refund")
private boolean refund;
#JsonProperty("void")
private boolean voidd;
#JsonProperty("error")
private GeneralErrorInfo error;
public TenderType getTender_type() {
return tender_type;
}
public void setTender_type(TenderType tender_type) {
this.tender_type = tender_type;
}
public boolean isAutocomplete() {
return autocomplete;
}
public void setAutocomplete(boolean autocomplete) {
this.autocomplete = autocomplete;
}
public boolean isSplit_payment() {
return split_payment;
}
public void setSplit_payment(boolean split_payment) {
this.split_payment = split_payment;
}
public boolean isRefund() {
return refund;
}
public void setRefund(boolean refund) {
this.refund = refund;
}
public boolean isVoidd() {
return voidd;
}
public void setVoidd(boolean voidd) {
this.voidd = voidd;
}
public GeneralErrorInfo getError() {
return error;
}
public void setError(GeneralErrorInfo error) {
this.error = error;
}
public ConfigResponse withError(GeneralErrorInfo error) {
setError(error);
return this;
}
}
What could be the issue here? What am I missing to get proper error in JSON format in response?
You have to define the code of an exception. Here you can find different variants of how to handle exceptions for REST: link

How to handle both reactive and mvc ServerHttpRequest in a same #ControllerAdvice?

I have code like this below :
MyExceptionHandler {
#ExceptionHandler(Exception.class)
public Object handleMvc(Exception ex, org.springframework.http.server.ServerHttpRequest request) {
return request.getbody;
}
#ExceptionHandler(Exception.class)
public Object handleReactive(Exception ex, org.springframework.http.server.reactive.ServerHttpRequest request) {
return request.getBody();
}
}
The code above produces IllegalStateException: Ambiguous #ExceptionHandler method mapped for Exception error.
Is there a way to handle both MVC and reactive requests in the ControllerAdvice? I'm working on a common project that works across multiple projects using different httprequest type.
**In spring boot using this**
package com.example.demo.exception;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
#ControllerAdvice
public class UserCVExceptionController {
#ExceptionHandler(value = UserCVNotfoundException.class)
public ResponseEntity<Object> exceptionMsg(UserCVNotfoundException exception) {
Map<String, String> headers=new HashMap<String, String>();
headers.put("Status", "404");
headers.put("Message", "User Not found");
headers.put("User", exception.getEmail());
return new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
}
}

JUnit test is not triggering method in Spring controller

I want to test my controller with the help of JUnit. I am new to this. I have written some code for this but it is not coming to my function listCard. My controller is:
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping("/v1/card")
#Configuration
public class CardManagementController {
private final static Logger LOG = LoggerFactory
.getLogger(CardManagementController.class);
#Autowired
ICardService cardService;
#RequestMapping(value = "", produces = RestURIConstants.APPLICATION_JSON, method = RequestMethod.GET)
public #ResponseBody GetCardResponse getCard(
#ModelAttribute #Valid GetCardRequest request, BindingResult results)
throws RuntimeException, ValidationException {
if (results.hasErrors()) {
LOG.error("error occured occur while fetching card response");
throw new ValidationException(
"Error Occoured while validiating card request");
}
GetCardResponse response = null;
response = cardService.getCard(request);
return response;
}
#RequestMapping(value = "", produces = RestURIConstants.APPLICATION_JSON, method = RequestMethod.POST)
public #ResponseBody AddCardResponse addCard(
#ModelAttribute AddCardRequest request, BindingResult results)
throws RuntimeException, ValidationException {
if (results.hasErrors()) {
LOG.error("error occured while adding the card");
throw new ValidationException(
"Error Occoured while validiating addcard request");
}
LOG.debug("add Card with POST method");
AddCardResponse response = null;
response = cardService.addCard(request);
return response;
}
#RequestMapping(value = "", produces = RestURIConstants.APPLICATION_JSON, method = RequestMethod.DELETE)
public #ResponseBody DeleteCardResponse deleteCard(
#ModelAttribute #Valid DeleteCardRequest request,
BindingResult results) throws RuntimeException, ValidationException {
if (results.hasErrors()) {
LOG.debug("error occured while delting the card");
throw new ValidationException(
"Error Occoured while validiating delete card request");
}
DeleteCardResponse response = null;
response = cardService.deleteCard(request);
return response;
}
#RequestMapping(value = RestURIConstants.LISTCARD, produces = RestURIConstants.APPLICATION_JSON, method = RequestMethod.GET)
public #ResponseBody ListCardResponse listCard(
#ModelAttribute #Valid ListCardRequest request) throws RuntimeException, ValidationException {
ListCardResponse response = null;
response = cardService.listCards(request);
return response;
}
#ExceptionHandler({ ValidationException.class})
#ResponseBody
public CPPException handleValidationException(ValidationException ex) {
LOG.error("Exception occoured",ex);
CPPException exception = new CPPException(ex.getMessage());
exception.setStatus(500);
return exception;
}
#ExceptionHandler({RuntimeException.class})
#ResponseBody
public CPPException handleException(RuntimeException ex) {
LOG.error("Exception occoured", ex);
CPPException exception = new CPPException("Internal Server Error");
exception.setStatus(500);
return exception;
}
}
and I have written the following code for testing:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.my.cpp.controller.CardManagementController;
import com.my.cpp.request.ListCardRequest;
import com.my.cpp.service.impl.CardServiceImpl;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:/spring/application-context.xml"})
public class CardApiTest {
private MockMvc mockMvc;
//#Autowired
private CardManagementController cm=new CardManagementController();
#Autowired
private CardServiceImpl cmr;
#Before
public void setUp() throws Exception {
mockMvc= MockMvcBuilders.standaloneSetup(cm).build();
}
#Test
public void testList() throws Exception{
final ListCardRequest lr=new ListCardRequest();
this.mockMvc.perform(get("/v1/card/list?"));
}
}
First - Remove the #Configuration annotation from your controller. It doesn't belong here.
Second - Consider using Mockito while testing, since you have a service injected in your controller. Your updated test class should look something similar as below
#RunWith(MockitoJUnitRunner.class)
public class CardApiTest {
private MockMvc mockMvc;
#InjectMocks
private CardManagementController cm;
#Mock
private ICardService cardService;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(cm).build();
// Instantiate cardListRequest and cardListResponse here
when(cardService.listCards(cardListRequest)).thenReturn(cardListResponse);
}
#Test
public void testList() throws Exception{
this.mockMvc.perform(get("/v1/card/list?"));
}
}
Let know in comments if you need further info / assistance.

Categories

Resources