ParamConverter based on HTTP header value - java

I am creating a Jersey application using Jersey version 2.27.
One of my resources is using #QueryParam to read a query parameter value.
I am creating a ParamConverter and ParamConverterProvider such that I can specify the type of the parameter:
#GET
public MyObject getObject(#QueryParam("myvalue") MyParamObject param) {
}
My issue is that the parsing of the query parameter depends on the value of an HTTP header.
How do I access the HTTP headers of the request inside a ParamConverter?
I have tried injecting the ContainerRequestContext inside the ParamConverterProvider, but this results in the following error:
java.lang.IllegalStateException: Not inside a request scope.

I managed to solve it by injecting a ServiceLocator inside the ParamConverterProvider and pass it to the ParamConverter. The ParamConverter can then fetch the ContainerRequestContext in the fromString() method:
public Message fromString(final String value) {
final ContainerRequestContext requestContext = injector.getService(ContainerRequestContext.class);
}

Related

Get request mapping object in spring interceptor, to get actual url string pattern

It might be hard to explain why, but I have this situation where I need to get the request url mapping string of currently requested url.
Like if I have a GET URL as "/Test/x/{number}"
I want to get "/Test/x/{number}" not "/Test/x/1"
can I get the actual declared url string in interceptor?
If this is possible how can I achieve this
You can implement a HanderInterceptor to intercept, pre or post, request and introspect the method being called.
public class LoggingMethodInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(LoggingMethodInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod method = (HandlerMethod) handler;
GetMapping mapping = method.getMethodAnnotation(GetMapping.class);
log.info("URL is {}", Arrays.toString(mapping.value()));
return true;
}
}
This will output, URL is [/hello/{placeholder}]
Full example can be found here, https://github.com/Flaw101/spring-method-interceptor
You could add more logic to introspect only certain methods, certain types of requests etc. etc.
I think that you can get it with reflection and getting #RequestMapping anotations.
for example when you use
#RequestMapping(value = "/Test/x/{number}", method = RequestMethod.GET)
the value is what you are looking for if I got it right!
You only must find the controller class type.
Its possible I think but I didn't test it.
Check this:
In a Spring-mvc interceptor, how can I access to the handler controller method?
First it may be solved if the HandlerMethod was right but if you get cast error then you must get the controller class [I think].
When you get the controller class then you can looking for the method with according #RequestMapping annotation.
So
1- Find the controller class type
2- Search all methods with in the class by reflection
3- Check method annotations with specified url and specified method [GET / POST]
4- select the best candidate
If you have more than two URL parameter this method is not good!

Adding parameters to #ExceptionHandler for MethodArgumentNotValidException in Spring

I have a Spring controller that validates incoming requests with hibernate validator.
When the request is invalid, MethodArgumentNotValidException is thrown by the validator. Would it be possible to add additional class as an argument to handler method for the exception?
This is what i have:
#RequestMapping(value = "/...", method = RequestMethod.POST)
#ResponseBody
public Response handleCustomObject(#Valid #RequestBody CustomObject obj) {
//..
}
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e) {
return getMissingMandatoryParametersResponse(e);
}
}
And i would need something like example bellow, however this doesn't work:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e, CustomObject obj) {
// do something with CustomObject
}
If you want to do something with the object which failed the validation in the exception handler, you can retrieve it from BindingResult like so:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e) {
CustomObject ce = (CustomObject) e.getBindingResult().getTarget();
// do something with CustomObject
}
You can also take a look at Spring JavaDoc for #ExceptionHandler annotation to see the list of supported exception handler method argument types:
Handler methods which are annotated with this annotation are allowed
to have very flexible signatures. They may have arguments of the
following types, in arbitrary order:
An exception argument: declared as a general Exception or as a more specific exception. This also serves as a mapping hint if the
annotation itself does not narrow the exception types through its
value().
Request and/or response objects (Servlet API or Portlet API). You may choose any specific request/response type, e.g. ServletRequest /
HttpServletRequest or PortletRequest / ActionRequest / RenderRequest.
Note that in the Portlet case, an explicitly declared action/render
argument is also used for mapping specific request types onto a
handler method (in case of no other information given that
differentiates between action and render requests).
Session object (Servlet API or Portlet API): either HttpSession or PortletSession. An argument of this type will enforce the presence of
a corresponding session. As a consequence, such an argument will never
be null. Note that session access may not be thread-safe, in
particular in a Servlet environment: Consider switching the
"synchronizeOnSession" flag to "true" if multiple requests are allowed
to access a session concurrently.
WebRequest or NativeWebRequest. Allows for generic request parameter access as well as request/session attribute access, without
ties to the native Servlet/Portlet API.
Locale for the current request locale (determined by the most specific locale resolver available, i.e. the configured LocaleResolver
in a Servlet environment and the portal locale in a Portlet
environment).
InputStream / Reader for access to the request's content. This will be the raw InputStream/Reader as exposed by the Servlet/Portlet
API.
OutputStream / Writer for generating the response's content. This will be the raw OutputStream/Writer as exposed by the Servlet/Portlet
API.

Return error to client from Jersey's parameter injection code?

I have a couple of custom parameters that get injected into REST method handlers using Jersey 2.9. The parameter injection is performed by extending Jersey's AbstractContainerRequestValueFactory and related classes, as such:
#Provider
public class ParamProviderFactory extends AbstractContainerRequestValueFactory {
#Context
private UriInfo uriInfo;
#Override
public Object provide() {
// do some work, error happened due to user giving wrong query string format
// IMPORTANT PART:
// I need to return an error to the user from here
return result;
}
}
The parameter value injected depends on Query strings given by the user. If the query string values are invalid, I need to return an error to the user.
The question is: How can I return an error to the user and abort the REST call handling from within the provide method?
Thanks!!
Use the following in the provide() method:
getContainerRequest().abortWith(yourResponse)

JAX-RS How to get a cookie from a request?

Consider the following method:
#POST
#Path("/search")
public SearchResponse doSearch(SearchRequest searchRequest);
I would like this method to be aware of the user who made the request. As such, I need access to the cookie associated with the SearchRequest object sent from the user.
In the SearchRequest class I have only this implementation:
public class SearchRequest {
private String ipAddress;
private String message;
...
And here is the request:
{
"ipAddress":"0.0.0.0",
"message":"foobarfoobar"
}
Along with this request, the browser sends the cookie set when the user signed into the system.
My question is how to access the cookie in the context of the doSearch method?
You can use the javax.ws.rs.CookieParam annotation on an argument of your method.
#POST
#Path("/search")
public SearchResponse doSearch(
SearchRequest searchRequest,
#CookieParam("cookieName") Cookie cookie
) {
//method body
}
The Cookie class used here is javax.ws.rs.core.Cookie but you don't have to use it.
You can use this annotation on any argument as long as is:
is a primitive type
is a Cookie (same as in the example above)
has a constructor that accepts a single String argument
has a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String))
havs a registered implementation of ParamConverterProvider JAX-RS extension SPI that returns a ParamConverter instance capable of a "from string" conversion for the type.
Be List<T>, Set<T> or SortedSet<T>, where T satisfies 2, 3, 4 or 5 above. The resulting collection is read-only.
These rules come from the documentation of the #CookieParam annotation as implemented in Jersey, the reference implementation of JAX-RS

Automatic conversion of JSON form parameter in Spring MVC 4.0

I am trying to build a Spring MVC controller which will receive a POSTed form with a parameter in JSON format, and have Spring automatically convert it to a Java object.
Request content type is application/x-www-form-urlencoded
The name of the parameter that contains a JSON string is data.json
This is the controller:
#Controller
public class MyController {
#RequestMapping(value = "/formHandler", method = RequestMethod.POST)
public #ResponseBody String handleSubscription(
#RequestParam("data.json") MyMessage msg) {
logger.debug("id: " + msg.getId());
return "OK";
}
}
And this is what the MyMessage object looks like:
public class MyMessage {
private String id;
// Getter/setter omitted for brevity
}
Perhaps not surprisingly, posting a form with parameter data.json={"id":"Hello"} results in HTTP error 500 with this exception:
org.springframework.beans.ConversionNotSupportedException:
Failed to convert value of type 'java.lang.String' to required type 'MyMessage'
nested exception is java.lang.IllegalStateException:
Cannot convert value of type [java.lang.String] to required type [MyMessage]: no matching editors or conversion strategy found
If I read the MappingJackson2HttpMessageConverter docs correctly, Jackson JSON conversion is triggered by Content-Type application/json, which I obviously cannot use since this is a form POST (and I don't control the POSTing part).
Is it possible to get Spring to convert the JSON string into an instance of MyMessage, or should I just give up, read it as a String and perform the conversion myself?
Spring invokes your #RequestMapping methods with reflection. To resolve each argument it's going to pass to the invocation, it uses implementations of HandlerMethodArgumentResolver. For #RequestParam annotated parameters, it uses RequestParamMethodArgumentResolver. This implementation binds a request parameter to a single object, typically a String or some Number type.
However, your use case is a little more rare. You rarely receive json as a request parameter, which is why I think you should re-think your design, but if you have no other choice, you need to register a custom PropertyEditor that will take care of converting the request parameter's json value into your custom type.
Registration is simple in an #InitBinder annotated method in your #Controller class
#InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.registerCustomEditor(MyMessage.class, new PropertyEditorSupport() {
Object value;
#Override
public Object getValue() {
return value;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
value = new Gson().fromJson((String) text, MyMessage.class);
}
});
}
In this particular case, we don't need all the methods of the PropertyEditor interface, so we can use PropertyEditorSupport which is a helpful default implementation of PropertyEditor. We simply implement the two methods we care about using whichever flavor of JSON parser we want. I used Gson because it was available.
When Spring sees that it has a request parameter that you requested, it will check the parameter type, find the type MyMessage and look for a registered PropertyEditor for that type. It will find it because we registered it and it it will then use it to convert the value.
You might need to implement other methods of PropertyEditor depending on what you do next.
My recommendation is to never send JSON as a request parameter. Set your request content type to application/json and send the json as the body of the request. Then use #RequestBody to parse it.
You can also use #RequestPart like this:
#RequestMapping(value = "/issues", method = RequestMethod.POST, headers = "Content-Type=multipart/form-data")
public String uploadIssue(#RequestParam("image") MultipartFile file, #RequestPart("issue") MyMessage issue)

Categories

Resources