retrieve data from post in JAX-RS - java

The server is sending data in JSON as HTTP POST, and I am using jax-rs to handle and retrieve data.
I could use #Pathparam or #queryparam based on what I wanted, but data doesnt like in either of those. Not in header either, if I am right, as they content something like content-type, date and some similar sort. How do I retrieve data from POST?
#POST
#Path("/foo")
public void foo(){ //do i need to put sth in parameter paranthesis to get?
//handle the data!! but how??
}

I suggest you take a look at JAX-RS Entity Providers. I will explain to you how marshalling and unmarshalling is done to and from the response and request stream, respectively.
Basically you have MessageBodyReaders and MessageBodyWriters (the former being the one the unmarshall from the request stream. How is works say you have a method like so
#POST
#Consumes(MediaType.TEXT_PLAIN)
public Response postString(String s) {}
The String s, like any other method parameter without an annotation is treated as the body of the request. (Note a method can only have one non-annotated parameter; that is a request can only have one body). So what happens is the JAX-RS (implementation's) runtime will look through the registry of providers (MessageBodyReaders to be exact) to look for one that can handle unmarshalling a body of type text/plain into a String. There are some standard readers for some standard types, and this is one that is available for free.
Now in the case of JSON, say we have this
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response postJson(String json) {}
This can be done also because there is a writer that can handle this. Basically a String parameters can be handled most of the time. It is not difficult to make an InputStream into a String, a String parameter we will usually get support for free.
But what if we want unmarshal to a different type, say Foo
class Foo {
String bar;
String baz;
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response postJson(Foo foo) {}
And we had JSON like
{ "bar" : "barValue", "baz": "bazValue" }
We can do this, but we need a custom reader. Luckily there are some already out there. We just need to add the library dependency to out project. For example, Jackson (I'd say the de facto JSON processor in Java) has a reader for us. It's in the dependency
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.5.0</version>
</dependency>
We can just register the JacksonJsonProvider into our application. Then we will be able to use POJOs from our JSON. (For more help on registering this, please provider more information about what JAX-RS implementation you are using, and show your application configuration, whether it's web.xml or Java config)
Some Resources:
See an answer here that describes Jackson use better (with documentation)
See here if you are confused about the #Consumes annotation

Related

How to register an existing Jersey MessageBodyReader Provider as handling additional Content Types?

I have a REST webservice that handles JSON requests. I want to implement against the SCIM spec, to allow an Azure AD integration to provision users within my application.
I've written the method:
#POST
#Path("/scim/Users")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
ScimUser createUser(#HeaderParam("Authorization") String authHeader,
ScimUser user);
However, AAD is making requests with Content-Type: application/scim+json, so my application is returning a 415: Media Type Not Supported error.
If I just swap over to
#Consumes({MediaType.APPLICATION_JSON, "application/scim+json"})
My application routes the request to the correct method, but still throws a 415 error, this time because it can't find an appropriate message body reader.
I have a MessageBodyReader that can handle the incoming messages; "scim+json" is fully parsable as JSON. However, I can't modify the library code to add a new #Consumes annotation onto the existing class.
Is there a way to let Jersey know that this class can handle these messages?
My current workaround is to create a new class:
#Provider
#Consumes(ScimService.SCIM_JSON_TYPE)
#Produces(ScimService.SCIM_JSON_TYPE)
public class ScimJsonProvider extends ExistingJsonProvider {
public ScimJsonProvider() {
super();
}
}
and register that, but this feels kind of hacky, and I'd like to know if there's a better way.
Edit: this also doesn't seem to work; it seems like this ScimJsonProvider is sometimes being selected as the MessageBodyWriter for application/json events, and then later is throwing a 415 because that's not a type registered in its #Produces list. :(

JAX-RS resource method signatures

I'm using Jersey 2.19 to implement a REST API.
I'd like to know how I find out from the Jersey user guide or other specification how I'm supposed to know what the signature of my JAX-RS resource should be.
E.g. for a resource that handles POST requests I've experimented with the following different signatures using examples I've found.
public Response myResource()
public Response myResource(String param)
Both of these are valid in that they compile and run and the method is called under the right conditions.
Can anyone tell me where it is specified what the signatures should be and what the parameters mean? It seems like a straightforward question but I can't find the answer.
As you are saying its a POST request , so it should recieve some data from the Request. So you should expect something in Parameter.
public Response myResource(String param)
But the type of parameter should depend upon actually #Consumes annotation like :-
#Consumes(MediaType.APPLICATION_JSON) : This expects a JSONinput OR
#Consumes(MediaType.APPLICATION_XML) : This expects a XMLinput OR
#Consumes(MediaType.TEXT_PLAIN) : This expects a String plain text input
You annotate your Methods like described in the official documentation.
Also, do not forget to annotate the service-class with #Path
#Path("MyService")
public class MyService
{
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/User")
public List<User> getUser()
{
//Return all users
}
//Inserts new User in JSON Format
#Get
#Path("/User/UserId/{userid}")
public User getUserById(#PathParam("userid") String userid)
{
//Find User with ID in Database and return it
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
public User getUserById(User user)
{
//add user to your Database or something
}
}
If you now want to get all users in json format you have to call:
http://ip-address/MyService/user
There is an exact answer to your question, but gird your loins. Because if the Jersey docs are overly vague these are in the extreme opposite direction: written by someone showing off their PhD in abstract algebra it looks to me.
The answer to everything is in the JAX-RS spec, of which Jersey is an implementation. You can download it here as PDF (after you sign away your soul)
The specific answer to how one of those methods is selected instead of the other, is too detailed for me to paste in here, but it's under section "3.7.2 Request Matching"
I won't even try to paste in the mathematical rules used to set up the list of potential methods to match a request, then select from among them. There's no chance of getting them formatted readably in SO.
For your more general questions, the section "3.3 Resource Methods" is much more accessible. Here are a few choice excerpts:
3.3 Resource Methods
...
JAX-RS defines a set of
request method designators for the common HTTP methods: #GET, #POST,
#PUT, #DELETE, #HEAD and #OPTIONS.
...
3.3.1 Visibility: Only public methods may be exposed as resource methods.
...
3.3.2 Parameters: Resource methods MUST have at most one entity parameter ...
3.3.3 Return Type: Resource methods MAY return void, Response, GenericEntity, or another Java type...
etc, etc.

Using JERSEY and JACKSON to parse a POST request with unknown structure

I'm in a current situations in which I have a REST endpoint that accepts POST of incoming JSON messages.
The thing is that I don't think I can specify the POJO object so Jackson can marshall the JSON into the POJO object. Reason for this is that I don't have control of what comes to that endpoint, and number of fields and type can change over time, thus, defining a POJO before hand seems not an option.
So I guess the question is....can I simply tell Jackson to don't do any marshalling and give the String of the response? I can deal with that later with JSONObject-JSONArray or Gson maybe. Say I'd have a method like this:
#POST
#Path("/callback")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response facebookUpdate(String json) {
//Do something with the json response...
}
If this is not feasible with Jersey-JAX...any other alternatives?
Thanks!
Alejandro
The easiest is to simply not inject the json into the method and use the request object instead:
public Response facebookUpdate(#Context request) {
try(InputStream is=request.getEntityInputStream()) {
...
}
}
From the request you can then get an inputstream for the request and parse it whichever way you like.
For parsing I can recommend my own jsonj library, which was written specifically to support open ended scenarios like you describe and uses jackson to deserialize into heavily customised implementations of java.util.Map and java.util.List. Gson is also a very solid choice.
If you want to do this application wide, you can instead write your own #Provider and do the same there. This is how I use my library currently actually.

Why use REST #Produces annotation

So I understand that you are specifying the type, but why? Under what conditions would it matter. For example, if I have the following method, and I comment out the #Produces annotation, it still returns JSON.
#GET
#Path("/json")
//#Produces({MediaType.APPLICATION_JSON})
public String getJson(){
return toJson(getResults());
}
The API doc says 'If not specified then a container will assume that any type can be produced.' So why would I not want the container to assume that?
I think it depends on your JAX-RS implementation but here's Jersey's explanation of their #Produces annotation: https://jersey.java.net/documentation/latest/jaxrs-resources.html#d0e1809
Basically, it's up to the client to determine what content type the server should spit back.
If the client supports more than one content type, you can sometimes specify the priority of content types to return, for a given method:
#Produces({"application/xml; qs=0.9", "application/json"})
In the above sample, if client accepts both "application/xml" and "application/json" (equally), then a server always sends "application/json", since "application/xml" has a lower quality factor.
If a client requests your JSON only resource with an Accept: application/xml; header then strictly speaking the server should return a 406 (not acceptable) status code, not the JSON string.
If you use the #Provides annotation, the container should handle this case for you and that is why you should use it.

#Consumes(MediaType.APPLICATION_JSON) annotation but getting request body as string

I am currently working on a project which was maintained by some other team and now i need to maintain it. While i was going through the project i found some thing below:
In jax-rs controller it was annotated by #Consumes(MediaType.APPLICATION_JSON) but the method takes request body as String rather than JSON. Then what is the use of the annotation? Does it help in content negotiation anyway?
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createCake(final String requestBody){.......}
How it is converting a JSON body to string?
My technology stack if it anyway helps to answer:
JAX-RS
Spring 3.2
Jersey 2.4
The #Consumes serves the following purpose. It restricts the mapping for your handlers. For example, you may have two handlers for the path /resource, one mapped to consume XML and the other mapped to consume json. The dispatcher will choose the right one based on the request's content-type.
The parameter type can be anything as long as there is an appropriate converter for the specified media type to the parameter type itself. In this case, there's very likely a converter from any media type to String.

Categories

Resources