java: Spring #RequestBody and #ResponseBody not working with java.net - java

Here is my java.net client code
URL server = new URL(serverUrl);
URLConnection connection = server.openConnection();
/* DoOutput flag is set to true to inform URL connection that we are
* sending data out. The default behavior is false. This will change
* the request from GET to a POST.
*/
connection.setDoOutput (true);
/* Setting the UseCaches to false allows the application "tunnel
* through" and ignore the caches.
*/
connection.setUseCaches (false);
connection.setRequestProperty ( "Content-Type", "application/octet-stream" );
ObjectOutputStream sendData = new ObjectOutputStream(connection.getOutputStream());
sendData.writeObject(packet);
sendData.flush();
sendData.close();
ObjectInputStream recieveData = new ObjectInputStream(connection.getInputStream());
response = (Serializable) recieveData.readObject();
System.out.println(response);
recieveData.close();
And this is my server code with spring to receive the request and send back response
#ResponseBody
#RequestMapping(value="/upload", method=RequestMethod.POST)
public Serializable fileUploadHandler(#RequestBody FilePacket packet, HttpServletRequest request, HttpSession session, HttpServletResponse response){
// handle request
return "response";
}
While running the code with above configuration, upon sending the request to server, I get Http error 415
java.io.IOException: Server returned HTTP response code: 415 for URL:
I removed #RequestBody and replaced it with
FilePacket packet = (FilePacket) new ObjectInputStream(request.getInputStream()).readObject();
and the request part was good. But then after a request I got the 406
java.io.IOException: Server returned HTTP response code: 415 for URL:
and so I replaced #ResponseBody with
new ObjectOutputStream(response.getOutputStream()).writeObject("response");
and then everything was good.
I've used these annotations with ajax and they work like charm but with java.net , I'm getting unsupported media type (415) and not acceptable (406). Is spring not able to read the request and send proper response or I'm missing something here? Is there a way to make it work?
Note: The class FilePacket is Serializable, one of whose fields is of type byte[] which holds the file data.

Spring uses a collection of HttpMessageConverter instances to deserialize request bodies into arguments it will pass to your handler method.
Spring does not have a such a HttpMessageConverter which converts application/octet-stream to your custom FilePacket class. You could instead use byte[] and do the conversion yourself (or provide your own HttpMessageConverter to do it).
Spring uses the same tactic for #ResponseBody, using these same HttpMessageConverter instances for serializing an object returned to the response body. It has nothing built-in which uses Java Serialization. Note that for most, if not all, of these response types, your client needs to provide an explicit Accept: header to specify what it accepts.

Related

How to send body as a Json in RestTemplate to get data from API [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
HTTP GET with request body
I've read few discussions here which do not advocate sending content via HTTP GET. There are restrictions on the size of data that can be sent via clients (web browsers). And handling GET data also depends on servers. Please refer section Resources below.
However, I've been asked to test the possibility to send content via HTTP GET using RestTemplate. I refered few discussions on spring forum but they were not answered. (Please note sending data via http Post works fine). The discussion here suggests using POST instead.
dev env - JBoss AS 5.1, Spring 3.1.3
Client
#Test
public void testGetWithBody()
{
// acceptable media type
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.TEXT_PLAIN);
// header
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
// body
String body = "hello world";
HttpEntity<String> entity = new HttpEntity<String>(body, headers);
Map<String, Object> uriVariables = new HashMap<String, Object>();
uriVariables.put("id", "testFile");
// Send the request as GET
ResponseEntity<String> result = restTemplate.exchange(
"http://localhost:8080/WebApp/test/{id}/body",
HttpMethod.GET, entity, String.class, uriVariables);
Assert.assertNotNull(result.getBody());
}
Server #Controller
#RequestMapping(value = "/{id}/body", method = RequestMethod.GET)
public #ResponseBody
String testGetWithBody(#PathVariable String id,
#RequestBody String bodyContent)
{
return id + bodyContent;
}
The problem -
executing this test case returns 500 Internal Server Error. On debugging, I found that the controller is not hit.
Is it correct to understand that the RestTemplate provides the way to send data as request body, but the error occurs because the server could not handle the request body ?
If the request body sent via HTTP Get is not conventional why does RestTemplate provide the APIs to allow sending it ? Does this mean there are few servers capable of handling the Request body via GET ?
Resources - discussions on sending body via HTTP GET using RestTemplate at spring forum
http://forum.springsource.org/showthread.php?129510-Message-body-with-HTTP-GET&highlight=resttemplate+http+get
http://forum.springsource.org/showthread.php?94201-GET-method-on-RestTemplate-exchange-with-a-Body&highlight=resttemplate+http+get
Resources - General discussions on sending body via HTTP GET
get-with-request-body
is-this-statement-correct-http-get-method-always-has-no-message-body
get-or-post-when-reading-request-body
http-uri-get-limit
Is it correct to understand that the RestTemplate provides the way to send data as request body, but the error occurs because the server could not handle the request body ?
You can tell by looking at network traffic (does the request get sent with a request body and a GET method?) and at server logs (the 500 result you receive must have a server-side effect that gets logged, and if not, configure the server to do so).
If the request body sent via HTTP Get is not conventional why does RestTemplate provide the APIs to allow sending it ? Does this mean there are few servers capable of handling the Request body via GET ?
Because it is a generic class that also allows you to craft requests that can include a message body.
As stated in HTTP GET with request body:
In other words, any HTTP request message is allowed to contain a message body, and thus [a server] must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.
A body on a GET cannot do anything semantically, because you are requesting a resource. It's like you tell the server: "Give me resource X, oh, and have some apples!". The server won't care about your apples and happily serve resource X - or throw an error because it doesn't like any offers in a request.
However, I've been asked to test the possibility to send content via HTTP GET
Please tell the one who requested this that this is a case that should not have to be tested, because no sensible implementation supports it.

Why am I getting a 406 error when trying to get JSON in Java Spring?

When I usually make controller endpoints with Java Spring that return JSON data, my return type is usually String; I'll toString() my JSON objects and parse them in my Javascript. Recently, I read somewhere that that wasn't the "correct" way to do it (trying to follow best practices and guidelines). I am starting a new project where my return type is a JSON object (dependency is org.json).
When I try to hit the endpoint in the browser, I'm getting a 406 error with a description of "The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.".
Here is what my endpoint looks like now:
#RequestMapping(value = "/api/get-items", method = {RequestMethod.GET, RequestMethod.POST}, headers = "Accept=application/json")
#ResponseBody
public JSONObject doGetItems(HttpServletRequest request, HttpServletResponse response) {
JSONObject data = new JSONObject();
JSONArray items = new JSONArray();
JSONObject item1 = new JSONObject().put("id", 123);
JSONObject item2 = new JSONObject().put("id", 456);
JSONObject item3 = new JSONObject().put("id", 789);
items.put(item1).put(item2).put(item3);
data.put("data", items);
return data;
}
How can I get this to work and return JSON? Or am I supposed to be returning a stringified version of my JSON object and I was doing this correctly all along?
Update
First, I tried taking out the headers parameter in the #RequestMapping annotation. I still got a 406 error.
I then tried changing/adding the parameters in the #RequestMapping annotation, with no luck as well. This is what I tried changing it to:
#RequestMapping(value = "/api/get-items",
method = {RequestMethod.GET, RequestMethod.POST},
headers = "Accept=*/*",
produces = MediaType.APPLICATION_JSON_VALUE)
I'm still getting a 406 error.
The HyperText Transfer Protocol (HTTP) 406 Not Acceptable client error response code indicates that the server cannot produce a response matching the list of acceptable values defined in the request's proactive content negotiation headers, and that the server is unwilling to supply a default representation.
Proactive content negotiation headers include:
A client (e.g. your Web browser or our CheckUpDown robot) can indicate to the Web server (running the Web site) the characteristics of the data it will accept back from the Web server. This is done using 'accept headers' of the following types:
Accept: The MIME types accepted by the client. For example, a browser may only accept back types of data (HTML files, GIF files etc.) it knows how to process.
Accept-Charset: The character sets accepted by the client.
Accept-Encoding: The data encoding accepted by the client e.g. the file formats it understands.
Accept-Language: The natural languages (English, German etc.) accepted by the client.
Accept-Ranges: Whether the client accepts ranges of bytes from the resource i.e. a portion of the resource.
By changing or removing header content at client side it should work.
When you specify headers = "Accept=application/json", then you're saying that an Accept header must be present and have exactly that value.
The client didn't send exactly that value, and there is not reason it should be required to do so, because the Accept header value is a comma-separated list of acceptable values, so it just needs to list application/json, it doesn't have to only specify that value.
To fix, replace headers = "Accept=application/json" with produces = "application/json".
Of course, the client still needs to specify that application/json, application/*, or */* is acceptable.

Spring 4 #RequestMapping -- consumes vs headers?

I'm learning how to build RESTful web services using Spring 4, and one thing I'm not clear on is in #RequestMapping. I've seen examples where one uses headers = "Accept=application/xml" and other examples using consumes (or produces) = "application/xml".
For instance, in my own #RestController class, I have this function...
// POST
#RequestMapping(method = RequestMethod.POST, headers = "Accept=application/xml")
public User create(#RequestBody User user) {
LOG.info("User = " + user.toString());
return userService.create(user);
}
What is the difference between using headers = "Accept=application/xml" vs. using consumes = "application/xml"? Or even using headers = "content-type=application/xml"?
Could someone explain the differences between headers and consumes/produces, and when each is used?
SHORT ANSWER
In the example you have above, using headers = "Accept=application/xml" or produces = "application/xml" will both respond to the client the same way i.e. send a response to the client with XML representation.
LONGER ANSWER
i. Headers
For RESTful web services, the client (e.g. your browser) sends a request (e.g. GET, POST, etc.) to a server, and the server will send a response back. This is an HTTP Transaction. Both the request and response have HTTP header fields ("headers"), which define the operating parameters of an HTTP transaction (I will refer to the headers for client request as "request headers", and these differ from headers from server response "response headers").
As part of the request your browser sends to server, there are different request headers and some examples include Accept, Connection, Content-Length etc. and each of these headers have their own function (see a full list of headers here: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields).
Using your code example, if a client does a POST request, Spring will check the request header(s) and if it finds a header Accept with a value of application/xml, it will map the request to the create method you have above (and in your case the server will return an XML response representation to the client).
Let me modify the headers element in the code you provided:
#RequestMapping(method = RequestMethod.POST, headers = "Connection=keep-alive")
public User create(#RequestBody User user) {
...
}
Notice the headers element now has a value of Connection=keep-alive. If a client does a POST request, Spring will check the request header(s) and if it finds a header Connection with a value of keep-alive, it will map that client request to the create method above.
ii. Produces and Consumes
If you used produces="application/xml" for the create method, this means a client request is only mapped to the create method if the client's Accept header matches application/xml. This essentially is the client saying, "Hey server, I prefer to accept your response in XML representation, so send your response to me in XML". Effectively, the produces="application/xml" is also the server saying, "Hey client, I can only produce responses for you in XML representation, so I will send you that format".
Link to Spring documentation reference.
If you used consumes="application/xml" for the create method, this means a client request is only mapped to the create method if the client's Content-Type header matches application/xml (the Content-Type request header describes the representation the client request is coming in). This essentially is the server saying, "Hey client, I can only consume requests in XML representation, so send that format to me".
SUMMARY
The headers element within the #RequestMapping annotation can take different request headers (Accept, Connection, Cache-Control etc.), but the produces element is only concerned with the Accept request header and the consumes element is only concerned with the Content-Type request header.
As the javadoc of HeadersRequestCondition (which handles the value provided in the headers attribute of a #RequestMapping annotation) states
Expressions passed to the constructor with header names 'Accept' or
'Content-Type' are ignored. See ConsumesRequestCondition and
ProducesRequestCondition for those.
So don't use those headers in headers. Use the produces and consumes attributes for Accept and Content-Type.
As to how to use them, the documentation gives examples: for consumes and for produces.

Is it possible to proxy an incomming Request?

I want to have two interceptors for every request I do on a webservice. One for the outgoing communication, and one for the response.
I am using ClientHttpRequestInterceptor which is working for the outgoing. I am setting it as follows:
//Rest template
RestTemplate tpl = api.getRestTemplate();
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add( new OutgoingRequestInterceptor() );
tpl.setInterceptors( interceptors );
However, I want something like this interceptor for the incoming (response). I checked Spring Framework sourcecode and I couldn't find anything for this.
Any tips?
Edit:
Maybe I am confused or something is wrong in my head. Im a bit ill today.
I've the following code in my interceptor class:
#Override
public ClientHttpResponse intercept( HttpRequest request, byte[] bytes, ClientHttpRequestExecution requestExecution ) throws IOException
{
SLog.d( "intercepted!!"+request.getURI()+". Bytes: "+bytes );
try
{
Thread.sleep( 5000 );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
ClientHttpResponse response = requestExecution.execute( request, bytes );
SLog.d( "Response Headers: " + response.getHeaders());
return response;
}
Question: Is this code working for outgoing, incoming, or both?
Because the log:request.getUri() is returning the destination URL.
Then, on the Response object, I get the headers sent by WebService.
So what I am sure of, is that response is actually the server response. But... How about getUri() thingy? Is it triggered before actually sending the request, or after?
Okay. After some tricky debugging, I got it.
Even though the interceptor class is called ClientHttpRequestInterceptor, it's intercepting both. Request from client, and respose from Server.
This interceptor class is something like a wrapper.
So...
This method is the wrapper of the whole request. From BEFORE request and after the request is done.
This part of the code is triggered BEFORE request is sent to webservice
This part of the code ACTUALLY CONTACTS WEBSERVICE, so it "pauses" there until it gets the response from the web service.
We return the response generated. Notice that if you use method response.getBody() which is an InputStream, you will consume it, so it will be null afterwards. I say that because you CAN'T directly log it. You've to mirror it first.

Passing JSON data in get request as request body

Hi i have to send a get request to an url
http://onemoredemo.appspot.com/group?authToken=access_token&authMethod=oauth
with request body contains json object as shown below.
{"goupid":"some_variable"
}
Here is a section of java code for sending get request:
URL url1=new URL("http://onemoredemo.appspot.com/group?authToken="+access_token+"&authMethod=oauth");
conn=(HttpURLConnection) url1.openConnection();
conn.addRequestProperty("Content-type", "application/x-www-form-urlencoded");
conn.setRequestMethod("GET");
conn.setDoOutput(true);
JSONObject jj=new JSONObject();
HttpGet get;
get.
jj.put("groupid", "testing#iritesh.com");
conn.addRequestProperty("Content-TYpe", "application/json");
conn.getOutputStream().write(jj.toString().getBytes());
conn.connect();
InputStream is=conn.getInputStream();
I am getting an error java.io.FileNotFoundException.
I sent a request from mozilla browser to url
http://onemoredemo.appspot.com/group?authToken=ya29.AHES6ZRDl-RqiA8W0PhybU_hMluHrHRjlJBvq06Vze0izJq0Ovjc088&authMethod=oauth
It was giving me correct response but now its more than one hour so acccesstoken expire. I know its weird to send parameter as well as requestbody in get request but i have to send it.
Please help in how to send a json object in request body in get request.
Don't do it.
Read this:
http://tech.groups.yahoo.com/group/rest-discuss/message/9962
"Yes. In other words, any HTTP request message is allowed to contain a
message body, and thus must parse messages with that in mind. Server
semantics for GET, however, are restricted such that a body, if any,
has no semantic meaning to the request. The requirements on parsing
are separate from the requirements on method semantics.
So, yes, you can send a body with GET, and no, it is never useful to
do so.
This is part of the layered design of HTTP/1.1 that will become clear
again once the spec is partitioned (work in progress)."
For other interesting discussions on this check this:
https://stackoverflow.com/a/978094/550967
https://stackoverflow.com/a/978173/550967
https://stackoverflow.com/a/978519/550967
The body of a GET request is not read.
Have you tried adding it to the params:
http://onemoredemo.appspot.com/group?authToken=access_token&authMethod=oauth&goupid=some_variable

Categories