I was using Spring version 4.x, and with the upgrade to 5.x - I have noticed that some of the API requests are failing (MissingRequestHeaderException) if the Accept header is not provided in the request.
The interface doesn't really need them, as it is not being used. The API's without it in the interface works fine. A solution would be to remove it from all the API's wherever it's there. But that's not a path we want to take now, I am looking for a general solution that could be applied to all API's without having to change each one separately.
Is there a way I could ask Spring to ignore this parameter in the interface ?
Or maybe handle the MissingRequestHeaderException so as to ignore it and process the API request, is that possible?
The API Interface:
public Void setEmployeeDetails( #PathVariable( "employeeId" )Integer employeeId, #Valid #RequestBody EDetails eDetails, String accept )
The API Controller implementing the interface:
Void setEmployeeDetails( #ApiParam(value = "ID of the employee.",required=true ) #PathVariable("employeeId") Integer employeeId,
#ApiParam(value = "" ,required=true ) #Valid #RequestBody EDetails eDetails,
#RequestHeader("Accept") String accept)
Javadoc of MissingRequestHeaderException says:
ServletRequestBindingException subclass that indicates that a request header expected in the method parameters of an #RequestMapping method is not present.
Checking the Spring source code, it also seems like that exception is only thrown by RequestHeaderMethodArgumentResolver, which says:
Resolves method arguments annotated with #RequestHeader except for Map arguments. See RequestHeaderMapMethodArgumentResolver for details on Map arguments annotated with #RequestHeader.
An #RequestHeader is a named value resolved from a request header. It has a required flag and a default value to fall back on when the request header does not exist.
So it would seem that if you're getting that error, it's because your code is asking for the Accept header and did not specify required=false, e.g.
#RequestMapping(...)
public void foo(#RequestHeader(name="Accept", required=false) String accept) {
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑
// Missing!
...
}
Related
Due to the way it was engineered in the past, there was one endpoint created for a specific parameter called foo in this case.
However the requirement meant that the endpoint could be used either with foo or a new parameter called bobby.
After trying to consolidate into one endpoint, the refactor work was too much.
So I opted for overloading the endpoint and using the spring boot trick to have the signature dictated by the request params.
Like so:
#GetMapping(params = {"foo"})
public CollectionResource<FooResource> get(#RequestParam("foo") String foo, ...) {} ...
#GetMapping(params = {"bobby"})
public CollectionResource<FooResource> get(#RequestParam("bobby") {} ...
This works well when interacting with the endpoints like so:
localhost:8080/testEndpoint?foo=bar
localhost:8080/testEndpoint?bobby=tables
However I discovered an edge case when trying the following:
localhost:8080/testEndpoint?bobby=tables&foo=bar
Which throws the following runtime exception
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/testEndpoint/':
This endpoint is not hit by users but programmatically, so its very low chance this case would happen. However is there a way to setup the controller so it can handle this and just throw a BadRequest etc. instead of blowing up?
Spring Boot Version : 1.5.16.RELEASE
Why not choose a primary endpoint?
For the first, just add the additional parameter to it
public CollectionResource<FooResource> get(#RequestParam("foo") String foo, ...
,#RequestParam("bobby")) {
By that first endpoint will be chosen in this corner case
Spring can not distinguish the endpoints on the basis of Request param.
Instead of two endpoint for serving two Request Param, have only one end point with two Request params. You have the options of making it not required.
#RequestParam("foo") String foo required = false, #RequestParam("bobby") String foo required = false
This gives you more simpler way to handle the API
try with exception handlers
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse("Server Error", details);
return new ResponseEntity(error, HttpStatus.BAD_REQUEST);
}
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.
I have followed this link for building CXF Restful webservices url link.
If suppose my url is as mentioned below :
http://localhost:8080/CxfRestService/rest/employeeservices/getemployeedetail?employeeId=1&empProfession=software
Here,"empProfession" parameter is optional for me.
So,eventhough if I omit that parameter and hit the below url, I should get the required response. http://localhost:8080/CxfRestService/rest/employeeservices/getemployeedetail?employeeId=1
Can anyone please help me out how to use optional parameters in the CXF Restful webservices.
Option 1 - Declare the parameter and check if != null
public Response getEmployeeDetail(#QueryParam("employeeId") String employeeId, #QueryParam("empProfession") String empProfession);
Option 2 - Declare en object to receive all known parameters
public Response getEmployeeDetail(#QueryParam("") EmployeeFilter filter) ;
public class EmployeeFilter {
public void setEmployeeId(String id) {...}
public void setEmpProfession(String p) {...}
}
Option 3 - Do not declare parameters and parse the URI. This option could be useful if you can accept non-fixed parameters
public Response getEmployeeDetail( #Context UriInfo uriInfo) {
MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
String employeeId = params.getFirst("employeeId");
String empProfession = params.getFirst("empProfession");
In fact, all parameters in CXF are not mandatory and you cannot change this using #QueryParam (as you can do compared e.g. with Spring-REST using #RequestParam(required=false)).
The solution is to add the #NotNull javax.validation annotation to indicate that a parameter is mandatory.
This way, you can use
CXF3 ValidationFeature to validate it automatically using #Valid.
CXF3 SwaggerFeature will also render mandatory parameters in the API documentation
CXF2 perform bean validation manually
See the CXF3 ValidationFeature for more info on using the javax.validation annotations: https://cwiki.apache.org/confluence/display/CXF20DOC/ValidationFeature
More about the CXF3 Swagger Feature here: http://cxf.apache.org/docs/swagger2feature.html).
This answer is related: Required #QueryParam in JAX-RS (and what to do in their absence)
I am currently setting up a Spring MVC application (version 4.1.4.RELEASE) and I want the application to return a JSON string on a 404 error rather than the default html response. I am using Tomcat 8 as my server. I have what I think should be correct, however it isn't behaving in the manner that I expect. What I'm trying to do is based off of this answer.
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
...
#Override
protected void customizeRegistration(ServletRegistration.Dynamic registration){
registration.setInitParameter("throwExceptionIfNoHandlerFound","true");
}
}
and then I have an exception controller (which is different than the question I based my solution off of, however I don't believe that is an issue as I am under the impression that #ControllerAdvice is an acceptable way to manage this based off of the Spring Docs. It looks something like:
#ControllerAdvice
public class GlobalExceptionController{
#ResponseStatus(value=HttpStatus.NOT_FOUND)
#ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Message handleMethodNotSupported(HttpServletRequest request){
...
}
#ResponseStatus(value=HttpStatus.NOT_FOUND)
#ExceptionHandler(NoSuchRequestHandlingMethodException.class)
public Message handleBadRequest(HttpServletRequest request){
...
}
#ResponseStatus(value=HttpStatus.NOT_FOUND)
#ExceptionHandler(NoHandlerFoundException.class)
public Message requestHandlingNoHandlerFound(HttpServletRequest request){
...
}
...
}
It continues to send back the default response. I know for a fact that it is hitting my customizeRegistration() function because breakpoints stop it, however, any breakpoints that I have in my GlobalException class are not hit. Also, the GlobalException class is within a package that is hit by a #ComponentScan() annotation, so I am fairly confident that it is also being handled by spring.
I assume I'm missing something obvious, any help would be greatly appreciated.
I don't think the return type you're trying to use is supported. Have you tried changing your return value to ResponseEntity or adding a #ResponseBody annotation?
From the docs:
A ModelAndView object (Servlet MVC or Portlet MVC).
A Model object, with the view name implicitly determined through a RequestToViewNameTranslator.
A Map object for exposing a model, with the view name implicitly determined through a RequestToViewNameTranslator.
A View object.
A String value which is interpreted as view name.
#ResponseBody annotated methods (Servlet-only) to set the response content. The return value will be converted to the response stream
using message converters.
An HttpEntity or ResponseEntity object (Servlet-only) to set response headers and content. The ResponseEntity body will be
converted and written to the response stream using message converters.
void if the method handles the response itself (by writing the response content directly, declaring an argument of type
ServletResponse / HttpServletResponse / RenderResponse for that
purpose) or if the view name is supposed to be implicitly determined
through a RequestToViewNameTranslator (not declaring a response
argument in the handler method signature; only applicable in a Servlet
environment).
I'm trying to get at the body of a POST, and I'd like the parameters of my method to bind to an object.
Is this possible?
My current declaration doesn't ever get hit:
#RequestMapping(method = RequestMethod.POST)
public void doStuff(#RequestBody byte[] bodyData, #ModelAttribute Form form, Model model ) {
Looks like I'm getting this exception:
- 2011-02-25 16:57:30,354 - ERROR - http-8080-3 - org.springframework.web.portle
t.DispatcherPortlet - Could not complete request
java.lang.UnsupportedOperationException: #RequestBody not supported
For this to work correctly, you have to be sure you're using AnnotationMethodHandlerAdapter. This overrides HandlerMethodInvoker's createHttpInputMessage (which is throwing the exception you're seeing). (It does this in a private class.)
I believe you can just include the following in your *-servlet.xml
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
WARNING: The below answer is for the case of needing #RequestBody and #RequestParam in the same handler method. It does not answer this question, but could be of use to someone.
I've tested this out using Spring 3.0.1. This is possible, but it's somewhat precarious. You MUST have your #RequestBody method argument before your #RequestParam argument. I'm guessing this is because HandlerMethodInvoker reads the request body (along with the GET parameters) when retrieving parameters (and the request body can only be read once).
Here's an example (WARNING: I code in Scala, so I've not compiled this Java code)
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(
#RequestBody String body,
#RequestParam("param1") String parma1,
Map<Object, Object> model: Map[AnyRef, AnyRef])
{
model.put("test", test)
model.put("body", body)
return "Layout"
}
An alternative is to use #PathVariable. I've confirmed that this works.
Unfortunately that is kind of impossible. If you are using portlet version of Spring MVC (and it looks like from the logs) then you might be interested in this JIRA issue.
AnnotationMethodHandlerAdapter uses PortletHandlerMethodInvoker internally and the second is a inner subclass of HandlerMethodInvoker - the place where you can configure HttpMessageConverter-s. But they're set to null. And the property is final.
That even would be to workaround if you could substitute HandlerMethodInvoker, but you can not.. it's constructor-created ;)
One thing to notice is that Servlet version of Spring MVC fully supports HttpMessageConverter-s and does not suffer this issue.