Small question regarding a curl request with multiple --data-raw, and how to construct its Java WebClient counterpart please.
I am having a very simple curl request:
curl -X GET https://some-url:8080/services/auth/login --data-raw username="some-user" --data-raw password="some-password"
The part I am having difficulties with is the double --data-raw, username="some-user" --data-raw password="some-password"
I first thought I could wrap them in a JSON, and combine with .body(BodyInserters.fromValue()) but that is not the case.
--data-raw '{"username": "some-user", "password": "some-password"}'
I then tried (based on this post comment):
webClient.mutate().baseUrl(someUrl).defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).build().get().uri(uriBuilder -> uriBuilder.path(someRoute).queryParam("username", "some-user").queryParam("password", "some-password").build()).retrieve().bodyToMono(String.class);
None of above worked.
How to send this equivalent of this curl request, using Java Spring WebClient please?
Thank you
Thanks to the comments, the correct use is:
Create a
private final MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
On the WebCLient:
webClient.mutate().baseUrl("https://...").build().post().uri("/route").headers(httpHeaders -> httpHeaders.setAll(headers))
Where headers is the formData
Related
I am creating a multipart RequestBody using OkHttp3. The following is the working curl request.
curl --location --request POST 'https://<url>' --form 'object=#<file_path>' --form 'config={"access":"YES"};type=application/json'
Removing ;type=application/json, produces an error from our Spring Boot server.
Content type 'application/octet-stream' not supported.
So it is clear that I should specify Json type for config. Let's create the Request using OkHttp.
String mimeType = URLConnection.getFileNameMap().getContentTypeFor(file.getName());
RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart(
"object", filename,
RequestBody.create(MediaType.parse(mimeType), file)
)
.addFormDataPart("config", "{\"access\":\"YES\"}") // CAN'T FIND A WORKING OPTION TO SPECIFY CONTENT TYPE HERE.
.build();
This produced the same error as mentioned above. So I changed the code as follows.
.addPart(
Headers.of("Content-Type", "application/json"),
RequestBody.create(MediaType.parse("application/json"), "{\"access\":\"YES\"}")
)
Now the OkHttp request builder throws this error.
Unexpected header: Content-Type
Using an empty header Headers.of(), creates the request body, but then I realized the form-data key config is not specified, from this API error.
Required request part 'config' is not present
I searched a lot, but can't find any solution with OkHttp, I found solutions in other libraries such as Spring RestTemplate.
This might work too.
.addFormDataPart(
"config",
null,
RequestBody.create(MediaType.parse("application/json"), "{\"access\":\"YES\"}")
)
After playing with this code, I got a way to specify the Content-Type.
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"config\""),
RequestBody.create(MediaType.parse("application/json"), "{\"access\":\"YES\"}")
)
I have got a basic curl like below:
curl -X POST \
'https://aogt.pl/auth/' \
-H 'Authorization: Basic NGZjMjExNWQyYTZk' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_id=4fc2115'
When I run it in the console on e.g. Ubuntu everything work correctly, I get a good response. Now I would like to map this curl into the java code using okhttp. I write below code:
public class TestMain {
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public static void main(String[] args) throws IOException {
String data = "client_id=4fc2115";
RequestBody body = RequestBody.create(JSON, data);
Request request = new Request.Builder()
.url("https://aogt.pl/auth/")
.addHeader("Authorization", "Basic NGZjMjExNWQyYTZk")
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.post(body)
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());
}
}
The pom file look like:
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.2.0</version>
</dependency>
</dependencies>
The problem is that when I run this code I get "400 Bad Request" so this is problem with server. I wrong map above curl into the java code into the http. Probably the problem is in POST body, because it is not JSON, but what I need to change here, could you please tell me what is wrong? Thank you very much.
The request you want to send has content-type as "application/x-www-form-urlencoded".
So creating the body as a JSON would not work.
You should try forming body in following way:
RequestBody body = new FormBody.Builder().add("client_id", "id_value").build();
RequestBody body = new FormBody.Builder() .add("client_id", "id_value");
Hi hope you had resolved this problem.
Actually, if u want to send a request with 'form-body' instead of 'json', you can easily use this class: 'FormBody'. It inherits RequestBody.
I am making a curl request to endpoint with following option curl --data {"foo" : "bar"} for POST request
However I want to implement a default behavior for missing data, ie curl without --data should also work for POST request.
Currently if data is missing it returns: "error":400,"errorCode":"INVALID_JSON". I do not want that.
Is it possible ?
Does it have any annotation to support optional data.?
I have the following scenario. I have an XML file:
query-users.xml
<?xml version="1.0"?>
<q:query xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3">
</q:query>
When executing the curl commend:
curl.exe --user administrator:5ecr3t -H "Content-Type: application/xml" -X POST http://localhost:8080/midpoint/ws/rest/users/search -d #C:\Users\user\query-users.xml
I get the desired response in XML.
I am trying to do the same POST request using RestTemplate from JAVA code:
try{
StringBuilder builder = new StringBuilder();
builder.append("http://localhost:8080/midpoint/ws/rest/users/search");
builder.append("?query=");
builder.append(URLEncoder.encode("<?xml version=\"1.0\"?><q:query xmlns:q=\"http://prism.evolveum.com/xml/ns/public/query-3\"></q:query>"));
URI uri = URI.create(builder.toString());
restOperations.postForEntity(uri, new HttpEntity<String>(createHeaders("username", "pass")), String.class);
logger.info(response);
}catch(Exception e){
logger.info(e.getMessage());
}
}
I get Internal Servel Error .
There is something that I am doing wrong passing the XML string to the POST request with RestTemplate, but I am not figuring out what it is.
Is there a way how to solve this?
Thanks
Your curl invocation and RestTemplate call are not equivalent. In first one you pass your xml as a a body of HTTP Request (this is what -d option does). In your RestTemplate you assign your xml to query and consequently HTTP Request has no payload and your data is encoded in URL.
If you want to pass your xml as a HTTP Body, you should use different HttpEntity constuctor: http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpEntity.html#HttpEntity-T-org.springframework.util.MultiValueMap-
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;
}