I'm developing an API for an application that is able to find so-called ProductSolutions based on their solutionID (which is an integer).
The code looks like this:
#Repository
public interface ProductSolutionRepository extends CrudRepository<ProductSolution, String> {
public List<ProductSolution> findBySolutionId(#Param("solutionId") int solutionId);
}
When sending a request with a string instead of an integer (localhost:8080/api/v1/productSolutions/search/findBySolutionId?solutionId=dangerousscript)the API returns an error with the following message:
Failed to convert from type [java.lang.String] to type
[#org.springframework.data.repository.query.Param int] for value
'dangerousscript'; nested exception is
java.lang.NumberFormatException: For input string:
\"dangerousscript\""`
Although it appears that Chrome and Firefox are neatly escaping the input (and don't execute any scripts) people are still afraid our API might be used for cross site scripting attacks.
A simple way to fix this would be removing the input the user gave when we throw an exception, or make our own error page. In what way can I configure spring boot to throw a custom exception?
You can define a method with #ExceptionHandler in class annotated with #ControllerAdvice.
You can either annotate this method with #ResponseBody (if you're not using #RestController) and directly return desired response or rethrow exception with correct message.
#ControllerAdvice
public class RestExceptionControllerAdvice {
#ExceptionHandler(NumberFormatException.class)
public ErrorResponse handleSearchParseException(NumberFormatException exception) {
// do whathever you want
}
}
Related
Is it possible to add some custom validation message to path variable?
I have some GET
#GetMapping("/v2/tw/{id}")
public TwDto getTw(Authentication auth, #PathVariable Long id) {
In case of /v2/tw/someString I'd like to catch error and add some custom error message like "invalid tw ID"... How to do that? In ControllerAdvice add some ExceptionHandler?
For your particular use case, you can use #ExceptionHandler in the Controller or in a #ControllerAdvice class as shown here. For example, I am returning NOT_FOUND error for the sake of it.
#ExceptionHandler({MethodArgumentTypeMismatchException.class})
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "this is the reason")
public void handle() {
}
You may not see the reason in the actual error response, until you enable
server:
error:
include-message: always
If you think your #ExceptionHandler is only needed in a Controller class you can keep the method inside the controller. Alternatively you can create a #ControllerAdvice class and put the method there, so that you can reuse across multiple controllers in your application.
However, if you want a more complex validation, I will suggest to keep the id type to String and then cast manually into Long and perform the validation. Doing so you can throw your own RuntimeException and handle different cases.
I defined the annotation #ExceptionHandler in my rest controller ,but it doesn't work as i wished when i mark the result type as text/plain,the method was defined to handle an attach upload request . SpringMvc just throw my business exception to the servlet container.Is it a bug of SpringMVC?How can i fix it?
#RestController
#RequestMapping("/api/test")
public class TestController extends BasicController{
#RequestMapping(value="/uploadAttach", headers = ("content-type=multipart/*"),method = RequestMethod.POST,produces="text/plain")
public String test(){
throw new ServiceException("biz exception");
}
#ExceptionHandler(value = {ServiceException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
public #ResponseBody StatusMessage serviceError(ServiceException ex) {
return new StatusMessage(ex.getMessage());
}
}
Since nobody has interest to answer this problem,i have to solve it myself.
I found that the exception handler will look for the right converter to handle the exception result which is defined as return value of the #ExceptionHandler. In my controller ,the exception is a pojo,when i debuged the program,i found there was no converter can write the pojo to type "text/plain" ,so SpringMvc throw the original exception to the servlet container,and returned an error page by Jetty.
you should follow these tips below:
1.you should define a customized exception,
but it must extend Exception.class;
2.setting a global exception hadler,and the dao throws the exception to service,the service throws to controller, then the controller uses the try...catch.. codes block to solve it;
3.a bean should be configured in SpringMVC.xml
like this:
4.and that's all, you can test it by using some simple codes like 1/0;
I have an endpoint in my controller configured as such:
#RequestMapping(value = "/users/{userId}/data", method = RequestMethod.GET)
public void getUserData(#PathVariable("userId") #Valid #NotNull Integer userId, HttpServletRequest request) {
}
If the client sends request to this endpoint with blank userId, I'm assuming Spring is interpreting the URL as /users//data because this exception is thrown:
2017-03-03 11:13:41,259 [[ACTIVE] ExecuteThread: '3' for queue:
'weblogic.kernel.Default (self-tuning)'] ERROR class
com.xxxx.web.controller.custom.ExceptionHandlingController:
RuntimeException thrown:
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException:
Failed to convert value of type 'java.lang.String' to required type
'java.lang.Integer'; nested exception is
java.lang.NumberFormatException: For input string: "data"
What is the best way to handle this use case? I'm wary about casting userId as a String and catching the exception, that seems like a hack. I do not want to rely on the client always sending a proper request either. I am currently using Spring-Boot v1.2.6.RELEASE and am willing to upgrade versions if I know that will fix it.
You can create a class to handle global exceptions and annotate it with #ControllerAdvice.
#ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandle {
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<Object> handleMethodArgumentTypeMismatch(
MethodArgumentTypeMismatchException ex, WebRequest request) {
//Handle your exception here...
}
}
Here is a good write up on how to a lot of things you can do with #ControllerAdivce
http://www.baeldung.com/global-error-handler-in-a-spring-rest-api
You have a conflict in your request mapping.
Most likely you also have GET mapping for /users/{userId}. This is the mapping which was applied instead of the one from your question.
The issue is that you request for /users//data, web server automatically replaces double slash by single slash. The result request exactly matches to this pattern /users/{userId} but not to the one you posted. Finally spring can't cast data as an integer.
If you remove /users/{userId} (just for test reasons) you'll probably get 404 error code requesting the same url.
EDIT:
In fact you should not care that someone sends wrong requests to your REST API. REST API is a contract and both sides should follow this contract. You as a backend point should only handle a request and provide appropriate error code and good description in case if the request is wrong. data was never a correct user-id make sure this information is included into the response instead of that technical stuff.
One of the possible solutions is to validate ids using pattern.
In your case it will look like this:
#RequestMapping(value = "/users/{userId:\\d+}/data", method = GET)
#RequestMapping(value = "/users/{userId:\\d+}", method = GET)
In this case spring will automatically filter non-number ids and provide HTTP 404 for them.
Using spring-web-4.2.6, I have the following Controller and ExceptionHandler:
#ControllerAdvice
public class ExceptionsHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDTO> HandleDefaultException(Exception ex) {
...
}
#ExceptionHandler(InternalError.class)
public ResponseEntity<ErrorDTO> HandleInternalError(InternalError ex) {
...
}
}
#RestController
#RequestMapping("/myController")
public class MyController {
#RequestMapping(value = "/myAction", method = RequestMethod.POST)
public boolean myAction() {
throw new InternalError("");
}
}
For some reason, the ExceptionsHandler's HandleDefaultException (for Exception.class) method is invoked, with an exception of type NestedServletException, instead of the HandleInternalError call.
When removing the default call, the IntenalError call is called with the proper InternalError exception.
I do not want to remove the default call as it is important to me to have a default handler to allow for a better experience for my users.
What am I missing here?
EDIT:
Apparently I'm using spring-web-4.3.3, without asking for it. I don't understand why exactly, here's my Gradle dependencies tree: http://pastebin.com/h6KXSyp2
Spring MVC should only exhibit the behavior you describe with version 4.3 and above. See this JIRA issue. Previously, Spring MVC would not expose any Throwable values to #ExceptionHandler methods. See
ExceptionHandler doesn't work with Throwable
Since 4.3, Spring MVC will catch any Throwable thrown from your handler methods and wrap it in a NestedServletException, which it will then expose to the normal ExceptionHandlerExceptionResolver process.
Here's a short description of how it works:
Checks if the handler method's #Controller class contains any #ExceptionHandler methods.
If it does, tries to resolve one that can handle the Exception type (including NestedServletException). If it can, it uses that (there's some sorting if multiple matches are found). If it can't, and the Exception has a cause, it unwraps and tries again to find a handler for that. That cause might now be a Throwable (or any of its subtypes).
If it doesn't. It gets all the #ControllerAdvice classes and tries to find a handler for the Exception type (including NestedServletException) in those. If it can, it uses that. If it can't, and the Exception has a cause, it unwraps it and tries again with that Throwable type.
In your example, your MyController throws an InternalError. Since this is not a subclass of Exception, Spring MVC wraps it in an NestedServletException.
MyController doesn't have any #ExceptionHandler methods, so Spring MVC skips it. You have a #ControllerAdvice annotated class, ExceptionsHandler, so Spring MVC checks that. The #ExceptionHandler annotated HandleDefaultException method can handle Exception, so Spring MVC chooses it to handle the NestedServletException.
If you remove that HandleDefaultException, Spring MVC won't find something that can handle Exception. It will then attempt to unwrap the NestedServletException and check for its cause. It'll then find the HandleInternalError which can handle that InternalError.
This is not an easy issue to deal with. Here are some options:
Create an #ExceptionHandler that handles NestedServletException and do the check for InternalError yourself.
#ExceptionHandler(NestedServletException.class)
public ResponseEntity<String> HandleNested(NestedServletException ex) {
Throwable cause = ex.getCause();
if (cause instanceof InternalError) {
// deal with it
} else if (cause instanceof OtherError) {
// deal in some other way
}
}
This is fine unless there's a bunch of different Error or Throwable types you want to handle. (Note that you can rethrow these if you can't or don't know how to handle them. Spring MVC will default to some other behavior, likely returning a 500 error code.)
Alternatively, you can take advantage of the fact that Spring MVC first checks the #Controller (or #RestController) class for #ExceptionHandler methods first. Just move the #ExceptionHandler method for InternalError into the controller.
#RestController
#RequestMapping("/myController")
public class MyController {
#RequestMapping(value = "/myAction", method = RequestMethod.POST)
public boolean myAction() {
throw new InternalError("");
}
#ExceptionHandler(value = InternalError.class)
public ResponseEntity<String> HandleInternalError(InternalError ex) {
...
}
}
Now Spring will first attempt to find a handler for NestedServletException in MyController. It won't find any so it will unwrap NestedServletException and get an InternalError. It will try to find a handler for InternalError and find HandleInternalError.
This has the disadvantage that if multiple controllers' handler methods throw InternalError, you have to add an #ExceptionHandler to each. This might also be an advantage. Your handling logic will be closer to the thing that throws the error.
I have a simple MVC controller that I annotate with my custom annotation:
#PreAuthorize("hasRole('GESTION_BENEFICIAIRE')")
#AuthentificationForte(otp = "#{args[0]}",transactionId="#{args[1]}")
#RequestMapping(value = "/ajouter", method = { RequestMethod.POST, RequestMethod.GET })
public String addBeneficiaire(#ModelAttribute("beneficiaireForm") BeneficiaireForm beneficiaireForm,
BindingResult result, Model model, Principal principal) {
[...]
}
My custom annotation is linked with an aspect that throws a RuntimeException when the validation doesn't succeed.
#Around(value = "#annotation(annotation)")
public Object verifyOtp(final ProceedingJoinPoint jointPoint,
final AuthentificationForte annotation) throws Throwable {
try {
if (authentificationForteEnabled) {
[...]
} else {
throw new AuthentificationForteException();
}
} else {
return jointPoint.proceed();
}
} finally {
}
}
So now the behavior is that when the validation fails, I am redirected to a 500 Error page. My goal is to stay in the same page and add a rejected message to the BindingResult:
result.rejectValue("suiteRib", "BeneficiaireForm.InvalidRib");
I haven't found a way to do that, the only way that I've found is to change all my logic and not use the annotation, while using a validation service with a try/catch in the controller code.
Is there any way to handle this and to access the binding result and add the error message when the aspect throws this exception?
Most definitely.
There is an example here of manipulating args:
Aspectj overwrite an argument of a method
You can also autowire services into the aspect class (remember to mark it #Configurable).
If you know the arguments before hand, then I think they can be included into the point cut definition, in which case they can be referred to directly in the around method. This is much nicer was as the arguments come strongly type.
You can read more here: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
May be its too late to answer your question but there are two ways you can handle it :
Have a try catch around proceed() in your aspect and when you get runtime exception you can either return the response from the aspect ( like a generic JSP showing the cause of error or generic JSON with error message.)
Second option could be to use Spring Exception Handler Controller advice. Spring MVC provides nice exception handler controller mechanism to call specific handler method for given type of exception class. ( Here is the blog link : https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc )
We currently have an application where we use mix of this approach to handle exception.