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
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.
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.
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.
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
After checking the documentation of Ceilometer and cURL I can suggest two things.
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")
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.
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;
}
Related
i'm creating java module to parse JSON file.
To receive file i need to send HTTP request. When I use curl my request looks like this:
curl -X GET "https://***" -H "accept: application/json" -H "apikey: ***"
How can I send the equivalent HTTP request from Java
Java has a lot of options to work with HTTP.
Option 1
Since Java 9, there is a built-in HTTP client. So You can use it to create a request without any third-party libraries.
A simple example is something like this:
HttpRequest request2 = HttpRequest.newBuilder()
.uri(new URI("some url"))
.header("someHeader", "value1")
.header("anotherHeader", "value2")
.GET()
.build();
For more examples see here
Option 2
Use third party libraries, there are many: OkHttpClient, More "old-school" Apache Http Client (HttpComponents
Option 3
If you're using spring, you might consider using Spring's WebClient. There are also wrappers in spring like RestTemplate that can come handy, but it really depends on what would you like to work with.
Many clients are coming with http connection pools that should be properly set up.
In addition, in your example, I see that you work with https - all these clients support it but it should be properly set up.
If you are using Spring then try WebClient - it is a bit harder to understand in the begging (at least harder than RestTemplate) but it pays of since RestTemplate will be discontinued.
You can find an example here
https://www.baeldung.com/spring-webclient-resttemplate
#GetMapping(value = "/tweets-non-blocking",
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Tweet> getTweetsNonBlocking() {
log.info("Starting NON-BLOCKING Controller!");
Flux<Tweet> tweetFlux = WebClient.create()
.get()
.uri(getSlowServiceUri())
.retrieve()
.bodyToFlux(Tweet.class);
tweetFlux.subscribe(tweet -> log.info(tweet.toString()));
log.info("Exiting NON-BLOCKING Controller!");
return tweetFlux;
}
Just be aware that this is non-blocking (e.g. asynchronous) solution so you won't get the response right away, but you subscribe to the request and then process the response when it is available. There are also blocking options in WebClient
Java has its own classes that allow you to send HTTP request. See class HttpURLConnection. However, I recommend using 3d party libraries that significantly simplify this task. Good libraries would be Apache Http client or OK Http client. I also can offer you to use another Open source library that has an HTTP client as well. It is called MgntUtils library and it is written by me. In this case your code would look something like this:
HttpClient workingClient = new HttpClient();
workingClient.setRequestProperty("accept", "application/json;charset=UTF-8");
workingClient.setRequestProperty("apikey", "***");
workingClient.setConnectionUrl("https://***");
ByteBuffer buffer =
workingClient.sendHttpRequestForBinaryResponse(HttpMethod.GET);
//or of your API returns contents of file as a string
String jsonStr = workingClient.sendHttpRequest(HttpMethod.GET);
After that, your ByteBuffer buffer or String jsonStr will hold the content of your JSON file. And now you can do whatever you need with it. Here is Javadoc for HttpClient class. The MgntUtils library can be obtained as maven artifacts here or on Github (including source code and Javadoc)
I am trying to complete the Instagram Oauth flow,
I currently have the authorization code which I'm to exchange for the access token. I am to make an x-www-form-urlencoded POST request to this endpoint
"https://api.instagram.com/oauth/access_token?"
This is what I've done so far.
String query = "https://api.instagram.com/oauth/access_token/?client_id=" + clientId +"&client_secret="+ clientSecret+ "&grant_type=authorization_code&redirect_uri="+ redirectUri + "&code=" + code
String response = new URL(query).getText()
A JSON string is expected as response.
Please Keep in mind that I'm a beginner.
I haven't read the Instagram documentation but based on your example code there's a couple of things to keep in mind:
you mentioned that you have to make a POST request, your example makes a GET request
never build a URL with untrusted parameter values. This basically means: always encode parameters, never trust them.
There are dozens of 3rd party HTTP Request libraries that give you flexibility and easier insight into aspects like timeouts and redirects. Java 11 has a built-in HTTP client that might ease this as well. But building on your code provided in your question using basic Java connection primitives this might work:
URL url = new URL("https://api.instagram.com/oauth/access_token/?client_id=${URLEncoder.encode(clientId, 'UTF-8')}&client_secret=${URLEncoder.encode(clientSecret, 'UTF-8')}&grant_type=authorization_code&redirect_uri=${URLEncoder.encode(redirectUri, 'UTF-8')}&code=${URLEncoder.encode(code, 'UTF-8')}")
def jsonString = ((HttpURLConnection) url.openConnection()).with {
setRequestMethod('POST')
setRequestProperty('Accept', 'application/json')
setDoInput(true)
connect()
if (getResponseCode() >= 400)
throw new Exception("Error code = ${getResponseCode()}")
inputStream.text
}
Every URL parameter is encoded so that any non-URL safe characters they contain are made safe, then we tell the connection that it will be a 'POST' and that we expect to get back json as input. inputStream.text is groovy code that takes an inputstream from the connection and reads all of the contents and then closes the stream. Since it is the last line of the with closure it is automatically returned as the value of the closure and assigned to the variable jsonString.
everyone!
I've been building and app that consumes an API from the company where I work. When I perform a request (GET) with URL connection (through HttpURlConnection) with header (encoding, authorization and etc.) AND a body (as a JSON, using Content-Type = "application/json") the response code is 200 and the connection works pretty good, but and I use the same header in another request BUT WITHOUT a content, I get a Bad Request.
I already tried setting the doOutout = false, but It doesn't work.
I think that everything is OK with the URL and the header because I performed the request by Postman and I got the response.
Here is the way I'm creating the connection and connecting:
val uri = Uri.parse("Base URL").buildUpon().appendPath(path).build() val url = URL(uri.toString()) val connection = url.openConnection() as HttpURLConnection connection.apply {
addRequestProperty("Charset", Charsets.UTF_8.displayName())
addRequestProperty("Authorization", "Basic $token")
addRequestProperty("X-Serial", 5036.toString())
requestMethod = "GET"
connectTimeout = 0
readTimeout = 0
doOutput = false
if (responseCode != HTTP_OK) throws Exception("Erro")
}
Bad Request suggest that it's an issue with the way your request is formed.
It is likely a problem with one of the request headers that you are setting. Have a look at the accepted request headers here.
Charset should potentially be named Accept-Charset, and I haven't seen any examples of X-Serial being used.
Here is an example of setting up a GET request using HttpUrlConnection. You could try removing all headers apart from Authorization and setting the request method to GET, like in this example.
I found out what was the problem: Me!
The $token value was generate with the flag DEFAULT. After I realized that, I putted the flag NO_WRAP and my request worked.
My bad! But thanks for all support!
I am communicating with a web service that expects a POST parameter and also expect Request body. I have confirmed that such a POST request can be done using a REST Console I have, but I am unable to make such a request in Java using Apache libraries.
In the code below, I am able to POST to the web service, and it correctly receives the contents of the variable raw_body. If I uncomment the first of the two commented lines, the web service receives the "fname" parameter, but it no longer receives the body of the POST.
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
...
HttpClient httpClient = new HttpClient();
String urlStr = "http://localhost:8080/MyRestWebService/save";
PostMethod method = new PostMethod(urlStr);
String raw_body = "This is a very long string, much too long to be just another parameter";
RequestEntity re = new StringRequestEntity(raw_body, "text/xml", "UTF-16");
//method.addParameter("fname", "test.txt");
//httpClient.getParams().setParameter("fname", "test.txt");
method.setRequestEntity(re);
How can I transmit both the parameter and the body?
You could use the setQueryString method to add the parameters to the URL that is being POSTed to. From a RESTful perspective I'd argue you should normally not be doing that, however, since a POST should represent a call to a resource and anything that would qualify for a query parameter should be included in the representation that is being transferred in the request body...or it should represent qualification of the resource itself in which case it should be part of the path that is posted to which could then be extracted by the controller using #PathVariable/#PathParam or something similar. So in your case you could also be looking for something like POST /MyRestWebService/files/test.txt or more fittingly a PUT if you're saving the resource and know the URI. The code on the server could pull the filename out from a URL pattern.
You need to make a POST request using multipart-form. Here is the example:
Apache HttpClient making multipart form post
Alternatively, you can make a POST request with the content (parameters and files) encoded using application/x-www-form-urlencoded but it is not recommended when you want to make a POST request with large content, like files.
I am trying to make a Http POST request using apache HTTP client. I am trying to copy contents of an HTTP POST request (received at my application) to another HTTP POST request (initiated from my application to another URL). Code is shown below:
httpPost = new HttpPost(inputURL);
// copy headers
for (Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements();) {
String headerName = e.nextElement().toString();
httpPost.setHeader(headerName, request.getHeader(headerName));
}
BufferedInputStream clientToProxyBuf = new BufferedInputStream(request.getInputStream());
BasicHttpEntity basicHttpEntity = new BasicHttpEntity();
basicHttpEntity.setContent(clientToProxyBuf);
basicHttpEntity.setContentLength(clientToProxyBuf.available());
httpPost.setEntity(basicHttpEntity);
HttpResponse responseFromWeb = httpclient.execute(httpPost);
Basically, I am trying to implement a proxy application which will get a url as parameter, froward the request to the URL and then serve pages etc in custom look and feel.
Here request is HttpServletRequest. I am facing problem in setting content length. Through debugging I found out that clientToProxyBuf.available() is not giving me correct length of input stream and I am getting Http error 400 IE and Error 354 (net::ERR_CONTENT_LENGTH_MISMATCH): The server unexpectedly closed the connection in chrome.
Am I doing it wrong? Is there any other way to achieve it?
The available() function doesn't provide the actual length of the content of the stream, rather
Returns the number of bytes that can be read from this input stream without blocking. (From javadoc)
I would suggest you to first read the whole content from the stream, and then set that to the content, rather than passing the stream object. That way, you will also have the actual length of the content.
It was rather simple and very obvious. I just needed to get content length from header as:
basicHttpEntity.setContentLength(Integer.parseInt(request.getHeader("Content-Length")));