3

i know you shouldn't send a HTTP GET Request with a body, but ceilometer web api forces me to do so. I'm developing a ceilometer scala client, so I need a scala/java way to make a get request with a body. So far I tried with beeClient (http://www.bigbeeconsultants.co.uk) and in plain Java using httpConnection but I get a 404 error. In curl I can achieve the result in this way:

curl -X GET -H "X-Auth-Token: ..long long token here.." 
-H "Content-Type: application/json" 
-d '{"q": [{"field": "resource", "op": "eq", "value": "gdfsf"}]}'
http://137.204.57.150:8777/v2/meters/

That's my scala code that uses java HttpURLConnection:

import java.io._
import java.net._
val token = "myToken"
val url = new URL("http://137.204.57.150:8777/v2/meters/")
val body = "{\"q\": [{\"field\": \"resource\", \"op\": \"eq\", \"value\": \"gdfsf\"}]}"
val bodyLenght = body.length.toString
val connection = url.openConnection().asInstanceOf[HttpURLConnection]
connection.setRequestMethod("GET")
connection.setRequestProperty("Content-Type", "application/json")
connection.setRequestProperty("Content-Length", bodyLength)
connection.setRequestProperty("Accept", "*/*")
connection.setRequestProperty("X-Auth-Token", token)
connection.setDoInput(true)
connection.setDoOutput(true)
//SEND REQUEST
val wr = new DataOutputStream(connection.getOutputStream)
wr.write(body.getBytes)
wr.flush
wr.close
if (connection.getResponseCode == 200) 
    println("ok")
else
    println("error")

What's the difference between my Java implementation and the curl command? I can't see any, I tried checking the header of curl calling it with the -v argument and that's what I get:

* Hostname was NOT found in DNS cache
*   Trying 137.204.57.150...
* Connected to 137.204.57.150 (137.204.57.150) port 8777 (#0)
> GET /v2/meters/ HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 137.204.57.150:8777
> Accept: */*
> X-Auth-Token: ...Token....
> Content-Type: application/json
> Content-Length: 60
> 
* upload completely sent off: 60 out of 60 bytes
* HTTP 1.0, assume close after body

And then I get the response.

Thank you in advance

tmnd91
  • 449
  • 1
  • 5
  • 22
  • Where is your code? HTTP spec doesnn't say anything about a body for GET, which means it is open for implementations to decide whether they want to allow body for GET or not. I believe you should be able to send a GET with a body form Java. – Juned Ahsan Nov 28 '14 at 00:41
  • HTTP Get requests DO NOT have a body. So you won't be able to do that. – Elliott Frisch Nov 28 '14 at 00:41
  • @ElliottFrisch I'm forced to do so, if you look at ceilometer web api docs here: [link](http://docs.openstack.org/developer/ceilometer/webapi/v2.html) you can find that users may pass query parameters inside the body of a get request. I think that this is possible with the python API and it's obvious possible in curl. I know that's it's not permitted by the standard to give semantic to a get request body. But I need to do so. – tmnd91 Nov 28 '14 at 09:25
  • Someone in the past already answered to a similar question in Java ( http://stackoverflow.com/questions/12535016/apache-httpclient-get-with-body ). Maybe you can make it work for scala? – Diego Martinoia Dec 01 '14 at 17:40
  • Also linked to this one http://stackoverflow.com/questions/978061/http-get-with-request-body?lq=1 – Diego Martinoia Dec 01 '14 at 17:51

6 Answers6

6

I resolved the problem using jetty-client implementation, that lets build http requests in anyway you want. Here's the code (it's not immutable but it's not that bad in scala):

val httpClient = new HttpClient()
httpClient.setConnectTimeout(connectTimeout)
httpClient.setFollowRedirects(false)
httpClient.setStopTimeout(readTimeout)
httpClient.start()
val resp = httpClient.newRequest(uri).
      method(HttpMethod.GET).
      header("X-Auth-Token",s).
      send()

Look that i'm using the blocking API but jetty provides also a Java NIO API.

2

I found a working plain java solution here, using apache's httpclient, httpcore, and commons-logging libs.

You need to create a class and extend HttpEntityEnclosingRequestBase, overriding the method name:

import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;

public class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {
    public final static String METHOD_NAME = "GET";

    @Override
    public String getMethod() {
        return METHOD_NAME;
    }
}

Then you just use it like this:

HttpClient httpClient = HttpClientBuilder.create().build();
HttpGetWithEntity e = new HttpGetWithEntity();
e.setURI(new URI(yourURL))
e.setEntity(yourEntity);
HttpResponse response = httpclient.execute(e);

Hope it helps.

Community
  • 1
  • 1
SebaGra
  • 1,783
  • 2
  • 27
  • 35
1

You use HTTP PUT or POST request when sending request body for Celiometer API.

I checked the Ceilometer documentation and found that all requests with request body use HTTP PUT or POST methods. No GET method with request body. http://docs.openstack.org/developer/ceilometer/webapi/v2.html

suztomo
  • 4,564
  • 1
  • 18
  • 21
  • False: look at the last curl command of this section: http://docs.openstack.org/developer/ceilometer/webapi/v2.html#api-queries – tmnd91 Dec 01 '14 at 15:27
  • I see your point. However still it allows you to use URL parameter in GET request. > "The query parameters should be encoded using one of the above methods, e.g. as the URL parameters or as JSON encoded data passed to the GET request." – suztomo Dec 01 '14 at 15:36
  • I put a lot of effort on a library able To parse and serialize complexqueries and stuff from and to json. I don't want to use the simple queries that can i do with get parameters – tmnd91 Dec 01 '14 at 15:47
  • I'm afraid that the best way is to avoid body in GET request and use simpler URI parameters. It's great that you know that we can use URL parameters for Ceilometer API of your requirement. – suztomo Dec 01 '14 at 23:20
1

In general, the specification does not prohibit body on any type of http request (GET, DELETE etc), so you can do it if needed. However by convention this is atypical.

The problem you're having is that there are assumptions about what you can and can't do in the implementation of URLConnection you're using. In general, you'll be using a HttpUrlConnection (as you cast to), which will actually be implemented by your jvm. For example, here is a sun specific implementation.

If you look at this implementation, you will see it assumes that a GET request where you need the output stream is actually a POST.

If you want a GET with a body, you need to use a different connection method, for example a library like apache-http-client. You could start by looking at this question. There may be better scala alternatives for you to start with.

Community
  • 1
  • 1
Nick
  • 1,782
  • 9
  • 9
0

After checking the documentation of Ceilometer and cURL I can suggest two things.

  1. Use URL parameters instead of JSON

As per the documentation you can use the URL parameters or JSON. You can modify your request as specified below to achieve the same thing with URL parameters rather than JSON.

URL("http://YOURHOST.COM:8777/v2/meters/?q.field=resource&q.op=eq&q.value=gdfsf")
  1. In case you have a specific reason not to use URL parameters for your JSON approach I guess encoding is what is missing in your request. Parameters are required to be sent in query parameters only rather than body content. For that I guess you need to try with below encoded data as shown below based on your request in the question.

    URL("http://YOURHOST.COM:8777/v2/meters/?q=%5B%7B%22field%22%3A%20%22resource%22%2C%20%22op%22%3A%20%22eq%22%2C%20%22value%22%3A%20%22gdfsf%22%7D%5D%7D")

Here q is the root query parameter name, without token I was not able to validate it.

Replace YOURHOST.COM with ip address for your server as it was showing problem to me even after putting them in code block and please let me know.

Sariq Shaikh
  • 1,045
  • 8
  • 24
  • I already tried passing the json as a query parameter but that doesn't work. I solved the problem passing the json encoded in the body of the GET request using jetty client. I couldn't achieve that in plain java because of the implementation of the HttpURLConnection that doesn't provide that functionality. – tmnd91 Dec 04 '14 at 09:55
  • So using Jetty Client is acceptable in your case ? Can you please share your code here of Jetty Client ? – Sariq Shaikh Dec 04 '14 at 19:18
  • Thank you but it doesn't show how are you passing the body or content for your query to ceilometer api – Sariq Shaikh Dec 05 '14 at 19:01
  • Sorry, i missed it, just add: content(new StringContentProvider(body)). send() – tmnd91 Dec 05 '14 at 21:18
0

you can try like this also

@RequestMapping(value = "/listcategories", method = RequestMethod.GET)

private ModelAndView getCategories() {
    ModelAndView modelAndView = new ModelAndView("list-of-categories");
    List<Category> categories = categoryService.getAllCategories();
    modelAndView.addObject("categories", categories);
    return modelAndView;
}
Christophe Roussy
  • 13,732
  • 2
  • 75
  • 75
user2595138
  • 53
  • 1
  • 9