I have a Get request: \allTopic
It inputs a filterCriteria custom object which contains multiple fields and one of those is an Enum called Role, which is either student or teacher
And there are many more different API calls with different Enums. So I want to create #controllerAdvice that can handle all these.
I need help with
Annotations to put in the Get API controller function header
Annotations to put in the filterCriteria class
What particular exception to handle in #exceptionHandler in #controlleradvice
I have some similar code as per your need, please have a look
#ControllerAdvice
public class ExceptionController {
#ExceptionHandler(value = PageNotFoundException.class)
public String pageNotFoundException(PageNotFoundException exception){
return "error/404";
}
#ExceptionHandler(value = AuthFailedException.class)
public String authFailedException(AuthFailedException exception){
return "error/401";
}
#ExceptionHandler(value = ServerException.class)
public String serverException(ServerException exception){
return "error/500";
}
}
Afterwards, you can throw any of the given type of exception.
please note, you have to create such your exception.
Example:
public class PageNotFoundException extends RuntimeException{
}
I hope, it helps!
Related
I have a simple SpringMVC controller
#RestController
#Validated
#RequestMapping("/users")
public class UsersController {
#GetMapping
public String getAllUsers(#Valid Filter filter) throws MyCustomServiceException {
[...]
}
}
Since this endpoint has around 20 RequestParam instead of bloating the controller(s) with all the fields I have put them all nicely in a POJO (that actually can be reused in other controllers that needs similar query params filters)
public class UserFilter extends GenericRequestParams {
[...]
#Email(message = "Hey")
private String email;
[...]
}
Here for example I'm using javax.validation.constraints.Email to validate one of the fields.
However when this validation fails instead of throwing the usual org.springframework.web.bind.MethodArgumentNotValidException it throws a org.springframework.validation.BindException
Could someone explain me why ?
So fare I have already dealt with javax.validation.ConstraintViolationException vs org.springframework.web.bind.MethodArgumentNotValidException (and I understood which is thrown when)
but now I also have org.springframework.validation.BindException that come join the party
right now I make custom handler using controlleradvice and is working well. I want to add additional info to the custom validation that "the invoice process has been failed". how do I achieve that ? thank you
You can define you own exception object and uses it to pass the data from the controller method to the exception handler method.
First define an exception as :
public class InvoiceException extends RuntimeException {
private Long invoiceId;
private String additionalInfo;
}
Then in the controller if you check that if violates the related business rules, create an instance of this exception and throw it :
public class InvoiceController {
#PostMapping("/invoice")
public Long processInvoice(InvoiceRequest request){
if(fail) {
throw new InvoiceException(invoiceId, "foobar");
}
}
}
In the #ContorllerAdvice , you can then access these data from the exception instance :
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(InvoiceException.class)
public ResponseEntity<ErrorMessage> handle(InvoiceException ex) {
}
}
The below is a service class
#Service
class Test {
public Object findEmployees1(String id, String dbId) {
return employeeRepo.findByDatabaseIdAndIdentifier(dbId, id);
}
public Object findEmployees2(String name, String dbId) {
Object records = employeeRepo.findByNameAndDatabaseId(name, dbId);
}
#ExceptionHandler(Exception.class)
public void internalErrorExceptionHandler(Exception ex) {
LOGGER.error("Log the exception here");
throw new InternalErrorException(ex.getMessage());
}
}
I want if any exception(eg some SQLException) comes in any of the method in the class test , it gets caught by the #ExceptionHandler and is logged there and re thrown
I cannot use #ExceptionHandler with #ControllerAdvice as I don't have any class with #Controller annotation on it . I am writing just a common module to get data from the database.
When I execute the code , the log under #ExceptionHandler is not printed as it never gets called.
The requirement is to log and rethrow a common exception if any exception comes in any of the service class methods without having individual try catch statements in each method.
As suggested, you can use spring aop to handle such exceptions. Instead of duplicating I will link to the existing question I have tried to answer here.
The other way is, if you are using ByteBuddy, you can use following annotation on the method which is expected to throw exception.
#Advice.OnMethodExit(onThrowable = RuntimeException.class)
I want to pass org.springframework.validation.Errors to CodeValidator class.
But, since I am not using RequestBody/RequestPart/ModelAttribute, I cannot put Errors in method param after variable.
I use #RequestParam for code variable, and I want to validate that using CodeValidator class that implement org.springframework.validation.Validator.
Here is my code
#RequestMapping(value = "/check-code", method = RequestMethod.POST)
public ResponseEntity<Object> checkCode(#RequestParam("code") String code, Errors errors) {
codeValidator.validate(code, errors);
if(errors.hasErrors()) {
return ResponseEntity.badRequest().body("Errors");
}
return ResponseEntity.ok("");
}
and here error result for my code:
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments to which they apply: public org.springframework.http.ResponseEntity com.example.myapp.controller.CodeController.checkCode(java.lang.String,org.springframework.validation.BindingResult)
what should I do to be able using CodeValidator with #RequestParam?
Updated:
Code for CodeValidator
#Service
public class CodeValidator implements Validator {
#Override
public void validate(Object target, Errors errors) {
String code = ((String) target);
if(code == null || code.isEmpty()) {
errors.rejectValue("code", "", "Please fill in Code.");
}
}
}
Did you create an annotation with your validator?
Otherwise take a look at a small example/tutorial for custom validating with spring: https://www.baeldung.com/spring-mvc-custom-validator
(edit) if you are using spring boot you might need add a MethodValidationPostProcessor bean to your spring config to enable custom valdation for the #requesParam
My controller. Note the custom #Exists annotation:
#RestController
public class ClientApiController {
#RequestMapping(path = "/{client}/someaction", method = RequestMethod.GET)
String handleRequest(#Exists Client client) {
// ...
}
}
The Exists annotation:
/**
* Indicates that a controller request mapping method parametet should not be
* null. This is meant to be used on model types to indicate a required entity.
*/
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Exists {}
The converter which converts the String from the path variable into a Client instance:
#Component
public class StringToClient implements Converter<String, Client> {
#Autowired
private ClientDAO clientDAO;
#Override
public Client convert(String source) {
return clientDAO.getClientById(source);
}
}
The ResourceNotFoundException exception used to trigger a 404
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
}
My controller method receives the converted Client as desired. If the client id used in the URL matches a client, everything works fine. If the id doesn't match, the client parameter is null empty (uses default constructor) in the handle() controller method.
What I can't get to work now is declarative checking that the Client is not null (i.e. that the id refers to an existing client). If it's null, a ResourceNotFoundException should be thrown. Checking whether the argument is null in the method body and throwing my custom ResourceNotFoundException is easy to do, but repetitive (like this one does). Also, this declarative approach should work for all model classes implementing the interface ModelWithId so it can be used for multiple model types.
I've searched the Spring documentation and I haven't found how to achieve this. I need to insert some processing somewhere after type conversion and before the controller's handleRequest method.
I'm using Spring Boot 1.3.3
After type conversion and before the controller's method there is a validation. You can implement custom validator and raise exception in it. Add new validator to DataBinder, and mark method's parameter as #Validated:
#RestController
public class ClientApiController {
#InitBinder
public void initBinder(DataBinder binder){
binder.addValidators(new Validator() {
#Override
public boolean supports(Class<?> aClass) {
return aClass==Client.class;
}
#Override
public void validate(Object o, Errors errors) {
Client client = (Client)o;
if(client.getId()==null) throw new ResourceNotFoundException();
}
});
}
#RequestMapping(path = "/{client}/someaction", method = RequestMethod.GET)
String handleRequest(#Validated #Exists Client client) {
// ...
}
#RequestMapping(path = "/{client}/anotheraction", method = RequestMethod.GET)
String handleAnotherRequest(#Validated #Exists Client client) {
// ...
}
}
Of course, you can declare validator as separate class, and use it repeatedly in other controllers. Actually, you can raise exception right in your converter, but there is possibility, that you'll need the conversion without exception in other places of your application.