I'm trying to update part of a legacy codebase I don't fully understand to work with a newer version of a REST API I also don't have access to the internals of. I do have a Swagger instance of it and can invoke it successfully via curl, but either Jersey is misbehaving or I'm misunderstanding how to read something.
If I execute the following curl command:
curl -v -k -X POST "[api endpoint]" -H "accept: application/json" -H "Authorization: Bearer [jwt token]" -H "Content-Type: multipart/form-data" -F "sender=[email address]" -F "recipient=[email address]" -F "fileType=file" -F "data=#[file]" -F "metaData=[other file]"
I get the following response:
> Content-Length: [#]
> Content-Type: multipart/form-data; boundary=------------------------9b1405ed70c2fd40
>
} [5 bytes data]
* We are completely uploaded and fine
{ [5 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 201 Created
< Date: Wed, 26 Aug 2020 00:29:56 GMT
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Set-Cookie: JSESSIONID=[ID];Path=/;Secure
< Keep-Alive: timeout=600
< X-Frame-Options: SAMEORIGIN
< Server: WildFly
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, DELETE, PUT
< Connection: Keep-Alive
< Access-Control-Allow-Headers: Content-Type
< Content-Type: application/json
< Location: [value I care about]
< Content-Length: 0
<
100 1667 0 0 100 1667 0 1825 --:--:-- --:--:-- --:--:-- 1825
* Connection #0 to host [proxy] left intact
This implies to me that POST responses which have empty bodies but nonempty headers are valid. However, when I try to effect the same thing via Jersey:
ClientConfig config = new ClientConfig();
config.register(MultiPartFeature.class);
Client client = ClientBuilder.newClient(config);
WebTarget endpoint = client.target([uri]);
FormDataMultiPart post = new FormDataMultiPart()
.field("sender", [email], MediaType.TEXT_PLAIN_TYPE)
.field("recipient", [email], MediaType.TEXT_PLAIN_TYPE)
.field("fileType", "file", MediaType.TEXT_PLAIN_TYPE)
.field("data", [InputStream], MediaType.APPLICATION_OCTET_STREAM_TYPE)
.field("metaData", [other InputStream], MediaType.APPLICATION_OCTET_STREAM_TYPE);
JsonNode response = endpoint.path([api path])
.request(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + [jwtTokenString])
.post(Entity.entity(post, MediaType.MULTIPART_FORM_DATA_TYPE), JsonNode.class);
I get a null response even though I know from inspecting server logs that the API command was received and processed successfully.
After a long time with the debugger I've determined that this is because Jersey will either return a null object or throw an exception if the data is null. (See here for more context. Though oddly enough I can't find the section they reference in any of the specification documents I can turn up via Google.)
This is probably fine as I'm not really interested in the empty body of the response, but I can't figure out how to get the HTTP headers returned as a result of my POST in Java.
You can't get the headers because you are using the method to only ask for the body as the response. What you want is the entire Response object. To get that, leave out the last argument (JsonNode.class) to the .post() method. That overloaded method you're using says that you don't care about anything but the body of the response. And that's what you will get.
When you use the overloaded post() method without the last body type parameter, the return type will be Response. You can get headers from it and the body.
Response response = endpoint.path([api path])
.request(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + [jwtTokenString])
.post(Entity.entity(post, MediaType.MULTIPART_FORM_DATA_TYPE));
URI locationUri = response.getLocation();
String locationHeader = response.getHeaderString("Location");
String body = response.readEntity(String.class);
Related
I have a server which replies first with status code 103 ( and some headers) and then with 200 ( plus some other headers) curl -I output below:
HTTP/2 103
link: <https://cdn.shopify.com>; rel=preconnect, <https://cdn.shopify.com>; crossorigin; rel=preconnect
HTTP/2 200
date: Mon, 08 Aug 2022 10:28:30 GMT content-type: text/html; charset=utf-8 x-sorting-hat-podid: 236 x-sorting-hat-shopid: 366627 x-storefront-renderer-rendered: 1 set-cookie: secure_customer_sig=;
My java.net.http.HttpClient seems to detect only the first status code (103) and reads only the first headers.
HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(Duration.ofMinutes(1))
.build();
HttpRequest request = HttpRequest
.newBuilder(httpReq, (name, value) -> true)
.header("user-agent", "Mozilla/5.0 (X11....")
//other headers here
.build();
HttpResponse<InputStream> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofInputStream()
);
logger.debug("call returned status code {}", response.statusCode());
The code above only shows 103 as status code ( which is an intermediary status code as you saw in the output of curl).
How do I skip that status code + its headers ? I'm only interested in the final (200) status code and the subsequent headers.
Unfortunately I have only a workaround which I will post here in order to help others : I changed the HTTP 2 to HTTP 1.1.
Feel free to find a better option . On my side I'm thinking about changing java's HttpClient with a more robust HttpClient implementation
i have a implementation for oauth 2.0 in Java, i wanted to try out some things with the Shopware 6 API. I can get the access token without problems and, as far as i see, i'm doing everything right to request a ressource with this access_token. In the header for the GET Request i put the 'Authorization Bearer' + access_token header and also the "Content-Type", "application/json" header.
HttpGet get = new HttpGet(resourceURL);
get.addHeader("Content-Type", "application/json");
and later
if (isValid(accessToken)) {
// update the access token
// System.out.println("New access token: " + accessToken);
oauthDetails.setAccessToken(accessToken);
// remove the old auth header
get.removeHeaders(OAuthConstants.AUTHORIZATION);
// add the new auth header
get.addHeader(OAuthConstants.AUTHORIZATION,
getAuthorizationHeaderForAccessToken(oauthDetails.getAccessToken()));
get.releaseConnection();
response = client.execute(get);
code = response.getStatusLine().getStatusCode();
The Error Code i always get is 415.
This is the complete response:
HttpResponseProxy{HTTP/1.1 415 Unsupported Media Type [Date: Thu, 04 Jul 2019 08:45:38 GMT, Server: Apache/2.4.25 (Debian), Cache-Control: no-cache, private, Access-Control-Allow-Origin: *, Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE, Access-Control-Allow-Headers: Content-Type,Authorization,sw-context-token,sw-access-key,sw-language-id,sw-version-id, sw-version-id: , sw-language-id: , sw-context-token: , x-frame-options: deny, X-Debug-Token: c1766c, X-Debug-Token-Link: http://localhost:8000/_profiler/c1766c, X-Robots-Tag: noindex, Vary: Authorization, Keep-Alive: timeout=5, max=100, Connection: Keep-Alive, Transfer-Encoding: chunked, Content-Type: application/json] ResponseEntityProxy{[Content-Type: application/json,Chunked: true]}}
The endpoint im trying to get is the "http://localhost:8000/api/v1/category/" endpoint. If im doing this whole thing with Insomnia/Postman i get the expected category information.
Does anyone have any idea what could be wrong? What am i missing here?
Please add following header
'Accept': 'application/json'
As nuriselcuk pointed out in the comment, the missing thing was the Accept header.
I added
post.addHeader("Accept", "application/json");
and now its working fine.
I make a basic post request to get a token in Java and I always get SSL certificate Handshake error :
DefaultHttpClient httpclient = new DefaultHttpClient();
String encoding = "YXBwLmJidmEuS3JlZGlsdafasdfasdf0bzpUeHZUUDJLVEdiKkhmbGNJeHNUDQ1d2tEU1dGak9TUk1zSVN3d2owYzJlJE9adU5rVmVZ";
HttpPost httppost = new HttpPost("https://connect.bbva.com/token?grant_type=client_credentials");
httppost.setHeader("Authorization", "Basic " + encoding);
HttpResponse response = httpclient.execute(httppost);
I have tried doing the same with Advanced Rest Client and a shell command and it works like a charm:
curl -X POST -i -H "Content-Type: application/json" -H "Authorization: Basic YXBwLmJidmEuS3JlZGl0bzpUeHZUUDJLVEdiKkhmbGNJeHNUNHU2RDE3MkFFa2R2QDQ1d2tEU1dGak9TUk1zSVN3d2owYzJlJE9adU5rVmVZ" https://connect.bbva.com/token?grant_type=client_credentials
I have also checked curl with the correct truststore option, which my default java takes to check if there is no problem with truststore and there aint one:
-cacerts=/usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts
Now I have used in Java java.net.URLConnection library too to test but it does not work.
I have also tested it with the content type but to no avail:
("Content-Type", "application/json;charset=UTF-8");
I have also tested this on java7 and java8, oracle both.
I have tried everything, I as a basic, mediocre java developer could have.
Btw, incase it helps, my curl response comes with the following headers:
HTTP/1.1 200 OK
Date: Thu, 18 May 2017 09:29:11 GMT
Server: Apache-Coyote/1.1
X-Frame-Options: DENY
Strict-Transport-Security: max-age=63072000; includeSubdomains; preload
Cache-Control: no-cache
Pragma: no-cache
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=UTF-8
Expires: 0
Pragma: no-cache
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Length: 751
I also checked the ssl-debug-logs using:
System.setProperty("javax.net.debug", "all");
and it uses the correct truststore.
I fixed this error just by changing the Java version from java-oracle8 to java-openjdk8.
I'm working on a java application. I need to call a remote api method. Suppose I have this information: remote_ip, remote_port, remote_method_name and some key-value data to post. I need to post my data to remote server through TCP protocol. I tested Sockets in this way, but not working:
Socket socket = new Socket(remote_ip, remote_port);
BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF8"));
String params = URLEncoder.encode("key1", "UTF-8")
+ "=" + URLEncoder.encode(value1, "UTF-8");
params += "&" + URLEncoder.encode("key2", "UTF-8")
+ "=" + URLEncoder.encode(value2, "UTF-8");
wr.write("POST " + remote_method_name + " HTTP/1.0\r\n");
wr.write("Content-Length: " + params.length() + "\r\n");
wr.write("Content-Type: application/x-www-form-urlencoded\r\n");
wr.write("\r\n");
wr.write(params);
wr.flush();
Could any one tell me how can I call api method in the correct way?
I want to do it without any third-party library if possible.
Any help would be gratefully appreciated.
First of all, even though you say you want to use raw tcp sockets, you're very clearly trying to make an HTTP rest request. It will be way easier and more appropriate to use an http client for that. I you don't want to use third-party libraries, use the built-in HttpUrlConnection (example usage).
Another advantage is that using an http client will give you a clear(er) error message.
Second, are you sure about that content-type? If you're trying to submit json, normally the header to set would be Content-Type: application/json.
Third, if you're getting 404 not found, I'd bet the url you're posting to is incorrect. Double-check the domain and baseurl with whoever gave you the specs for this API. Right now your URL is essentially http://remote_ip:remote_port/remote_method_name which is quite unlikely to be correct.
i think the reason is that the 'remote_method_name' you provided is wrong.
since you are making a http call, here is a brief example for you to refer.
for the page you are reading now, the request should be:
curl -v 'http://stackoverflow.com/questions/40171522/java-do-json-remote-procedure-callrpc-from-client'
* Trying 151.101.193.69...
* Connected to stackoverflow.com (151.101.193.69) port 80 (#0)
> GET /questions/40171522/java-do-json-remote-procedure-callrpc-from-client HTTP/1.1
> Host: stackoverflow.com
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
< Last-Modified: Fri, 21 Oct 2016 09:01:29 GMT
< X-Frame-Options: SAMEORIGIN
< X-Request-Guid: 405a2900-543b-4a97-8c62-8fa9019ab934
< Content-Length: 77809
< Accept-Ranges: bytes
< Date: Fri, 21 Oct 2016 09:18:59 GMT
< Via: 1.1 varnish
< Age: 0
< Connection: keep-alive
< X-Served-By: cache-ams4437-AMS
< X-Cache: MISS
< X-Cache-Hits: 0
< X-Timer: S1477041539.483029,VS0,VE95
< X-DNS-Prefetch-Control: off
< Set-Cookie: prov=aef7ece4-db49-60e0-3209-a2a2830d8749; domain=.stackoverflow.com; expires=Fri, 01-Jan-2055 00:00:00 GMT; path=/; HttpOnly
<
<!DOCTYPE html>
<html itemscope itemtype="http://schema.org/QAPage">
......
I have a RequestMapping setup in a spring #Controller and I am unable to correctly send a CURL request to it. I keep getting a HTTP 400 return code when trying to send various ways. I am not sure what is setup incorrectly.
Here is the request
curl -v -X POST localhost:8082/api/registerDevice -d '{"serial":"FA541YJ05065"}' -H "Content-Type:application/json;charset=UTF-8"
Here is the output
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying ::1...
* Connected to localhost (::1) port 8082 (#0)
> POST /api/registerDevice HTTP/1.1
> Host: localhost:8082
> User-Agent: curl/7.49.0
> Accept: */*
> Content-Type:application/json;charset=UTF-8
> Content-Length: 23
>
* upload completely sent off: 23 out of 23 bytes
< HTTP/1.1 400 Bad Request
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Thu, 16 Jun 2016 02:48:50 GMT
< Connection: close
<
{"timestamp":1466045330556,"status":400,"error":"Bad Request","exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"Could not read document: Unexpected character (''' (code 39)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')\n at [Source: java.io.PushbackInputStream#3bae0839; line: 1, column: 2]; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character (''' (code 39)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')\n at [Source: java.io.PushbackInputStream#3bae0839; line: 1, column: 2]","path":"/api/registerDevice"}* Closing connection 0
Spring RequestMapping
#RequestMapping(value = "registerDevice", method = RequestMethod.POST)
public ResponseEntity<String> registerDevice(#RequestBody Map<String, Object> payload) throws Exception {
Update
As mentioned below it was an issue with escaping the quotes and related to windows. The same CURL command worked fine on centos.
curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{"""serial""":"""FA541YJ05065"""}" http://localhost:8082/api/deregisterDevice
On Centos
curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"serial":"FA541YJ05065"}' http://localhost:8082/api/registerDevice
I suspect that you end up sending ' at the start of the json. You can try using double quotes around the json and escaping them in it.
See also
curl -v -X POST localhost:8082/api/registerDevice -d "{\"serial\":\"FA541YJ05065\"}" -H "Content-Type:application/json;charset=UTF-8"
The only reason behind this is that fact that your request is not formatted correctly
Check out this Spring 4.x/3.x (Web MVC) REST API and JSON2 Post requests, how to get it right once for all? for more information.