My requirement is to get the json response with customized error message when a required #RequestParam is not sent to the request handler or invalid parameter(required is int but user is passing string) is sent to the request handler.
currently I am trying to use the #Exceptionhandler mechanism to handle these exceptions. But the respective exception handler methods not getting invoked.
Please see the code snippet:
#Controller
#RequestMapping("api/v1/getDetails")
public class Abc {
#RequestMapping
#ResponseBody
public Envelope<Object> retrieveTransactions(#RequestParam(required = false) Integer a,
#RequestParam int b, #RequestParam(required = false) boolean c,
HttpServletRequest req) {`
//implementation goes here
}
#ExceptionHandler(MissingServletRequestParameterException.class)
#ResponseBody
public Envelope<Object> missingParameterExceptionHandler(Exception exception,
HttpServletRequest request) {
Envelope<Object> envelope = null;
//error implementation
return envelope;
}
#ExceptionHandler(TypeMismatchException.class)
#ResponseBody
public Envelope<Object> typeMismatchExpcetionHandler(Exception exception, HttpServletRequest request) {
Envelope<Object> envelope = null;
//error implementation
return envelope;
}
Do I need to configure anything extra for exception handler? can anyone tell me where I am doing the wrong.
Consider identifying the parameter name in the RequestParameter annotation.
For example
#RequestParam(value="blammy", required=false)
I've never bothered figuring out how to handle type mismatch,
instead I've found it easier to accept all parameters as String and perform all verification myself (including type).
Also,
If you are accepting the HttpServletRequest as a parameter to your handler,
then there is no need to use #RequestParam annotations,
just get the parameter values directly from the request.
Finally,
consider org.springframework.web.context.request.WebRequest
or org.springframework.web.context.request.NativeWebRequest
instead of HttpServletRequest.
Have you tried to use MethodArgumentNotValidException or HttpMessageNotReadableException instead on your handlers?
And put required = true on your #RequestParam declaration to catch missing params exceptions
#RequestParam(required = true)
Related
I have a web service written in Spring MVC. It can be used by 3rd party developers.
Our methods have a lot of optional parameters (passed in the query string).
I want to make sure that all the query string parameters are spelled correctly and there is no typos.
Is there an easy way to do it? Method signature example:
#RequestMapping(value = {"/filter"}, method = RequestMethod.GET)
#ResponseBody
public List<MetricType> getMetricTypes(
#RequestParam(value = "subject", required = false) Long subjectId,
#RequestParam(value = "area", required = false) Long areaId,
#RequestParam(value = "onlyImmediateChildren", required = false) Boolean onlyImmediateChildren,
#RequestParam(value = "componentGroup", required = false) Long componentGroupId
) throws Exception
{
//Some code
}
If somebody calls this method with "onlyImediateChildren=true" parameter (a typo) instead of "onlyImmediateChildren=true", Spring MVC will ignore the typoed parameter and will assume "onlyImmediateChildren" is null. Developer will get slightly incorrect list of results and will not notice the error. Such issues could be widespread and difficult to diagnose. I want to check there is no typoed params in query string to prevent such issues.
UPDATE
It is possible to extract the list of actual parameters from the query string. Then it could be compared with the list of the allowed parameters. If I hardcode the allowed parameter list, it will duplicate the method signature. I wonder if it is easy to extract a list of allowed parameters from the method signature (e.g. by #RequestParam annotation)?
Many thanks
Maxim
You could implement your own HandlerInterceptor. In preHandle method you can obtain all HandlerMethod's parameters annotated with #RequestParameter. These will be all allowed parameters in request.
Here is my implementation of an HandlerInterceptor which will only accept the parameters which are explicitely defined by a parameter annotation:
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.HandlerInterceptor
/**
* Interceptor which assures that only expected [RequestParam]s are send.
*/
#Component
class UnexpectedParameterHandler : HandlerInterceptor {
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (handler is HandlerMethod) {
val queryParams = request.parameterNames.toList()
val expectedParams = handler.methodParameters
.map { methodParameter ->
val requestParamName = methodParameter.getParameterAnnotation(RequestParam::class.java)?.name
val parameterName = methodParameter.parameter.name
requestParamName ?: parameterName
}
val unknownParameters = queryParams.minus(expectedParams)
if (unknownParameters.isNotEmpty()) {
response.writer.write("unexpected parameter $unknownParameters")
response.status = HttpStatus.BAD_REQUEST.value()
return false
}
}
return super.preHandle(request, response, handler)
}
}
You could use the getParameterMap method of the request to get a Map of all the submitted parameters, and validate the keys against a list of all allowed parameters. You should be able to get the request object by simply adding it to the method signature, e.g.:
public List<MetricType> getMetricTypes(
HttpServletRequest request,
#RequestParam(value = "subject", required = false) Long subjectId,
...
) throws Exception {
Spring will inject all the query parameters present in the url string through the argument of type
#RequestParam Map<String,String> in your controller method, if present.
#RequestMapping(value = "", method = RequestMethod.GET, produces = {"application/json"})
public HttpEntity<PagedResources<WebProductResource>> findAll(#RequestParam Map<String, String> allRequestParams){
...
}
You can then validate the keys of the map yourself. For an "enterprisey" way to do that generically, see my answer here: How to check spring RestController for unknown query params?
I am using spring annotation in my java code - with eclipse IDE
in my Controller i have this method:
#RequestMapping(value = "/some/link", produces = "application/json")
#ResponseBody
public AnyDto suggestMapping(#Valid #RequestBody(required = false) SomeDto dto) throws Exception {
///some code
return null;
}
How could i view the Full http request in eclipse expression window (during debug)
when i add #RequestBody to expression window i get an error ?
To view the request, you can add HttpServletRequest request to your method signature, e.g.:
public AnyDto suggestMapping(#Valid #RequestBody(required = false) SomeDto dto, HttpServletRequest request) throws Exception {
///some code
return null;
}
Spring then automatically 'autowires' HttpServletRequest request into the method code for you.
#ResponseBody is different - it simply takes the return type (AnyDto) and will set it as the HTTP response body (i.e. so it's unrelated to the request).
My controller looks like the following:
#RequestMapping(value = "/cars/{types}", method = RequestMethod.PUT,
headers = "Accept=application/json")
#ResponseStatus(HttpStatus.OK)
public void startEngine(
#PathVariable #Min(0) String types, #RequestBody #Valid someObject request, BindingResult result)
throws MethodArgumentNotValidException {
if(result.hasErrors())
{
System.out.println("Error");
//Should I be throwing MethodArgumentNotValidException here? And if so how? I don't know how to retrieve the first parameter for it's constructor (MethodParameter object)
}
//Controller code
}
So after I verify whether or not my result object encountered any errors during validation, how can I then throw the MethodArgumentNotValidException? Or should Spring be already throwing that exception during validation?
If I remember correctly, Spring should throw MethodArgumentNotValidException only if you have not provided an Errors (here, BindingResult) parameter for the #Valid annotated parameter.
You can throw it yourself if you would like to.
I'm new to Jersey, and want to determine the #Produces type in other contexts, so I can use it during error handling cases.
For example, I have the following method that produces json:
#Path("test-json")
#Produces(MediaType.APPLICATION_JSON)
#GET
public Object getTestJson(#Context HttpServletRequest req, #Context HttpServletResponse res) throws Exception
{
throw new RuntimeException("POST submitted without CSRF token! ");
}
Later on, in a global exception handler, I'd like to get the #Produces media type.
I've tried doing this with something like the following, but getMediaType() is returning null (note that this is simplified, but headers is not null in all of my tests, just getMediaType() is null).
public class someClass
{
#Context
HttpHeaders headers;
public Response convertExceptionToResponse(T exception)
{
MediaType mediaType = headers.getMediaType();
// At this point, I thought media type would be
// MediaType.APPLICATION_JSON
// for the above 'getTestJson' method, but it's null.
}
}
How can I do this?
JAX-RS
Inject ResourceInfo and invoke getResourceMethod() which will return Java Method. Then you can simple retrieve declared annotations. The problem here is that with this approach you need to do a lot of coding in case #Produces is not located directly on a method but somewhere in the hierarchy.
Jersey 2
Inject ExtendedUriInfo
#Context
private ExtendedUriInfo uriInfo;
and look for matched ResourceMethod (getMatchedResourceMethod()). Then simply get list of producible media types (getProducedTypes()).
I have a web service written in Spring MVC. It can be used by 3rd party developers.
Our methods have a lot of optional parameters (passed in the query string).
I want to make sure that all the query string parameters are spelled correctly and there is no typos.
Is there an easy way to do it? Method signature example:
#RequestMapping(value = {"/filter"}, method = RequestMethod.GET)
#ResponseBody
public List<MetricType> getMetricTypes(
#RequestParam(value = "subject", required = false) Long subjectId,
#RequestParam(value = "area", required = false) Long areaId,
#RequestParam(value = "onlyImmediateChildren", required = false) Boolean onlyImmediateChildren,
#RequestParam(value = "componentGroup", required = false) Long componentGroupId
) throws Exception
{
//Some code
}
If somebody calls this method with "onlyImediateChildren=true" parameter (a typo) instead of "onlyImmediateChildren=true", Spring MVC will ignore the typoed parameter and will assume "onlyImmediateChildren" is null. Developer will get slightly incorrect list of results and will not notice the error. Such issues could be widespread and difficult to diagnose. I want to check there is no typoed params in query string to prevent such issues.
UPDATE
It is possible to extract the list of actual parameters from the query string. Then it could be compared with the list of the allowed parameters. If I hardcode the allowed parameter list, it will duplicate the method signature. I wonder if it is easy to extract a list of allowed parameters from the method signature (e.g. by #RequestParam annotation)?
Many thanks
Maxim
You could implement your own HandlerInterceptor. In preHandle method you can obtain all HandlerMethod's parameters annotated with #RequestParameter. These will be all allowed parameters in request.
Here is my implementation of an HandlerInterceptor which will only accept the parameters which are explicitely defined by a parameter annotation:
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.HandlerInterceptor
/**
* Interceptor which assures that only expected [RequestParam]s are send.
*/
#Component
class UnexpectedParameterHandler : HandlerInterceptor {
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (handler is HandlerMethod) {
val queryParams = request.parameterNames.toList()
val expectedParams = handler.methodParameters
.map { methodParameter ->
val requestParamName = methodParameter.getParameterAnnotation(RequestParam::class.java)?.name
val parameterName = methodParameter.parameter.name
requestParamName ?: parameterName
}
val unknownParameters = queryParams.minus(expectedParams)
if (unknownParameters.isNotEmpty()) {
response.writer.write("unexpected parameter $unknownParameters")
response.status = HttpStatus.BAD_REQUEST.value()
return false
}
}
return super.preHandle(request, response, handler)
}
}
You could use the getParameterMap method of the request to get a Map of all the submitted parameters, and validate the keys against a list of all allowed parameters. You should be able to get the request object by simply adding it to the method signature, e.g.:
public List<MetricType> getMetricTypes(
HttpServletRequest request,
#RequestParam(value = "subject", required = false) Long subjectId,
...
) throws Exception {
Spring will inject all the query parameters present in the url string through the argument of type
#RequestParam Map<String,String> in your controller method, if present.
#RequestMapping(value = "", method = RequestMethod.GET, produces = {"application/json"})
public HttpEntity<PagedResources<WebProductResource>> findAll(#RequestParam Map<String, String> allRequestParams){
...
}
You can then validate the keys of the map yourself. For an "enterprisey" way to do that generically, see my answer here: How to check spring RestController for unknown query params?