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

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

Related

Managing any HTTP request in a generic way

In my organisation, when I want to expose an API, I have to declare it with a swagger contract, same for any update, and it can take multiple weeks before the creation or change is taken into account.
That's why we've come with the idea to declare only one contract for all the APIs we need to expose, and manage the routing in an applicative reverse proxy (the request would include the necessary metadata to allow to route to the appropriate endpoint) :
{
"genericHttpRequest" : base64encodedByteArrayOfAnyHttpRequest
}
Now the question is :
how to manage this request without reimplementing HTTP ? Is it possible to put back the array of byte into a structured HttpServletRequest ?
/**
* Manage a generic request
*/
#RequestMapping(value = "/genericRequest", method = RequestMethod.POST)
public #ResponseBody void manageGenericRequest(#RequestBody GenericHttpRequestDto body) {
byte[] genericHttpRequest = body.getGenericHttpRequest();
//(...)
}
Spring will inject a HttpServletRequest if it is set as a method parameter. Furthermore, wildcard path mappings will enable the methods to be matched to every request:
#RestController
#RequestMapping("/generic-endpoint/**")
public class DemoController {
#RequestMapping
public ResponseEntity<Object> genericGetRequest(HttpServletRequest httpServletRequest) {
return ResponseEntity.ok().body(httpServletRequest.getMethod());
}
}
Optionally, you could return a ResponseEntity to gain more control over your HTTP response.

#QueryParam or HttpServletRequest in java

I have resource class - UserResource interface and I have defined an endpoint as getUsers.
Now I want to filter these users based on users status - (Active, Inactive) and its not mandatory so if I does not pass the status it will fetch all the users.
Now the question is should I pass this as #QueryParam or get it from
HttpServletRequest - httpServletRequest.getParameter("Status").
Which one from the above two is best way and in what scenario I should use which one.
1. First way is pass the status as query param and define in the resource file itself. Here UserResource is the controller interface or resource class. In getUsers method has #QueryParam.
import javax.ws.rs.core.Response;
#Path(/user)
public interface UserResource{
#GET
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
Response getUsers(#QueryParam("status") String status);
}
#Component
Public class UsersResourceImpl implement UserResource{
public Response getPlan(String status){
String userStatus = status;
// some logic
}
}
2. Second way is get the query param from HttpServletRequest. so I have
autowired the HttpServletRequest and getting the query param from the
httpservletrequest.
import javax.ws.rs.core.Response;
#Path(/user)
public interface UserResource {
#GET
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
Response getUsers();
}
import javax.servlet.http.HttpServletRequest;
#Component
Public class UsersResourceImpl implements UserResource{
#Autowired
private HttpServletRequest httpRequest;
public Response getPlan(String status){
String status = httpRequest.getParameter(status)
// some logic
}
}
'''
Well, I honestly don't see any appealing reason to avoid using the #QueryParam annotation given that you need the value from a query parameter.
Some benefits of using #QueryParam that I can think of:
The #QueryParam annotation will automatically bind the value(s) of a query parameter to a resource method parameter, resource class field, or resource class bean property. So you won't need to extract and parse parameters manually, once you respect some rules described in the documentation:
The type T of the annotated parameter, field or property must either:
Be a primitive type
Have a constructor that accepts a single String argument
Have a static method named valueOf or fromString that accepts a single
String argument (see, for example, Integer.valueOf(String))
Have 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 or 4 above. The resulting collection is read-only.
#QueryParam can be combined with #DefaultValue to define a default value for the parameter in case it's not present in the request.
If multiple endpoints support the same query parameters, you could aggregate them in a class and receive an instance of such class as a #BeanParameter.
Go with annotations (i.e #QueryParam) that's why we chose such framework , remember convention over configuration.

ParamConverter based on HTTP header value

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);
}

REST Subresource Locators

I am reading through the book RESTful Java with JAX-RS 2.0, 2nd Edition and am struggling to understand how Subresource Locators work, below is a cut-down version of the example provided.
CustomerDatabaseResource class
#Path("/customers")
public class CustomerDatabaseResource {
#Path("{database}-db")
public CustomerResource getDatabase(#PathParam("database") String db) {
// find the instance based on the db parameter
CustomerResource resource = locateCustomerResource(db);
return resource;
}
protected CustomerResource locateCustomerResource(String db) {
...
}
}
CustomerResource Class
public class CustomerResource {
private Map<Integer, Customer> customerDB =
new ConcurrentHashMap<Integer, Customer>();
private AtomicInteger idCounter = new AtomicInteger();
public CustomerResource(Map<Integer, Customer> customerDB)
{
this.customerDB = customerDB;
}
#GET
#Path("{id}")
#Produces("application/xml")
public StreamingOutput getCustomer(#PathParam("id") int id) {
...
}
So I understand that as a request such as GET /customers/northamerica-db/333 comes in, will first match the expression on the method CustomerDatabaseResource.getDatabase() which based upon the location, will create the correct instance of CustomerResource.
What I don't understand is what happens next...
The instance resource gets returned, but returned to where?
How does the web service know to then match and process the remaining part of the request with the method CustomerResource.getCustomer()? I guess this is because The CustomerDataBaseResource class doesn't have a #GET, but I don't really understand how the transition happens.
Is this specific to RESTEasy?
The instance resource gets returned, but returned to where?
It's get's returned to the request processing engine and continues to look for a matching method (inside the return resource object), just like any other request.
How does the web service know to then match and process the remaining part of the request with the method CustomerResource.getCustomer()? I guess this is because The CustomerDataBaseResource class doesn't have a #GET, but I don't really understand how the transition happens
Resource locators aren't supposed to be annotated with Http Methods. That's how they are known to be locators. Since it is not the resource method to be called, it should not be annotated. Imagine this
public class CustomerResource {
#PUT
#Path("{id}")
public Response updateCustomer(Customer customer) {}
#POST
#Path("{id}")
public Response createCustomer(Customer customer) {}
}
If CustomerDataBaseResource.getDatabase() were to be annotated with an Http method, then we couldn't hit the above methods. All the locator needs is the #Path, and the URI matching will continue starting from that path.
/customers/database-us
Once the CustomerResource is created, if the request uri is /customers/database-us/123, then now the next logical step is to find a matching resource method based on the URI, so will be looking for something annotated with #Path that will match 123. Then the Http method is checked.
Is this specific to RESTEasy?
Going through the jax-rs spec, I don't see anything about sub-resource locators, but Jersey also implements this exact behavior. I've read the book you are referring to, and from what I remember, the author doesn't really get much into anything that is implementation specific, but does mention common feautres that most implementers implemented, that is not part of the spec. Maybe this is one of those things.
UPDATE
So it is in the spec. Go to the link and download the spec. You will find everything under 3.4.1 Sub Resources and some algorithm info for request matching in 3.7.2 Request Matching

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