Follow original HTTP Method - What's the java fix? - java

Postman has a setting which corrects the method on calls which redirect.
Postman Settings : Follow original HTTP Method
Follow original HTTP Method: Redirect with the original HTTP method
instead of the default behavior of redirecting with GET.
How can we implement this in Java code? I am using OKHttp. I am also open to using other libs if necessary.
Error:{"detail": "Method "GET" not allowed."}
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("job_id","z5G7h3l6a1kMvyS65NP3c0a2-MRSuDJL0EWa7zDjsGs=")
.addFormDataPart("firstname","Jhon")
.addFormDataPart("lastname","Doe")
.addFormDataPart("email","john.doe#gmail.com")
.addFormDataPart("city","Dallas")
.addFormDataPart("state","TX")
.addFormDataPart("country","US")
.addFormDataPart("mobile_number","555-5555")
.addFormDataPart("file_name","jd.pdf")
.addFormDataPart("resume","/C:/Users/test/Downloads/test.pdf",
RequestBody.create(MediaType.parse("application/octet-stream"),
new File("/C:/Users/test/Downloads/test.pdf")))
.build();
Request request = new Request.Builder()
.url("https://api.ceipal.com/xx-yy-zzz/ApplyJobWithOutRegistration")
.method("POST", body)
.build();
Response response = client.newCall(request).execute();
This results in these calls in the interceptor:
url: https://api.ceipal.com/xxx-yyy-zzz/ApplyJobWithOutRegistration
method:POST
header:Content-Type: multipart/form-data; boundary=21938c2b-0d98-4851-8532-a91ba219491d
Content-Length: 157614
Host: api.ceipal.com
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.9.1
url: https://api.ceipal.com/xxx-yyy-zzz/ApplyJobWithOutRegistration/
method:GET
header:Host: api.ceipal.com
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.9.1
as the redirect replaces the POST with GET the call responds with this error:
Error:{"detail": "Method "GET" not allowed."}
When I try this call in Postman,
Without the setting I get a response: Error:{"detail": "Method "GET" not allowed."}
With the setting turned on - {"status": 201, "success": 0, "message": "You have already applied for this job."}

Related

Spring RestTemplate GET request returns 302 status

I am trying to consume a third party REST API using Spring's RestTemplate component. I have tried entering the same request on an external REST API Client (Postman) - using the same URI and custom headers and I am able to retrieve the correct data.
However, when I tried to mirror the exact request using RestTemplate, it returns me
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved here.</p>
<hr>
<address>Apache/2.4.7 (Ubuntu) Server at address Port 80</address>
</body></html>
This is a sample of the code I am using:
String uri = "http://address/{path of endpoint}";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set(someCustomHeaderKey, someCustomHeaderValue);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
I have read that java does not allow redirect from one protocol to another, for instance, from http to https and vise versa. Would require some help on the approach on this.
RestTemplate will follow redirects by default, but not if the protocol is different,
which is the situation that you are seeing (redirecting from http to https).
For a more full explanation, and code that makes this work, see
HTTPURLConnection Doesn't Follow Redirect from HTTP to HTTPS
I have tried your code on my local machine and everything seems fine.
302 status code is indicating that your URI location is different.
as per your example, you should use https instead of Http in URI
I have tried your code as below
String uri = "https://jsonplaceholder.typicode.com/todos/1";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("link", "http/:");
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET,
entity, String.class);
System.out.println(response);
output in console
<200,{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
},[Date:"Thu, 12 Mar 2020 03:45:44 GMT", Content-Type:"application/json; charset=utf-8", Content-Length:"83", Connection:"keep-alive", Set-Cookie:"__cfduid=d3104b8bbd25cbcb802977fc9183d559e1583984744; expires=Sat, 11-Apr-20 03:45:44 GMT; path=/; domain=.typicode.com; HttpOnly; SameSite=Lax", X-Powered-By:"Express", Vary:"Origin, Accept-Encoding", Access-Control-Allow-Credentials:"true", Cache-Control:"max-age=14400", Pragma:"no-cache", Expires:"-1", X-Content-Type-Options:"nosniff", Etag:"W/"53-hfEnumeNh6YirfjyjaujcOPPT+s"", Via:"1.1 vegur", CF-Cache-Status:"HIT", Age:"1747", Accept-Ranges:"bytes", Expect-CT:"max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"", Server:"cloudflare", CF-RAY:"572a862eab83d5e8-BOM"]>

apache cxf MultiPart request has no Content-Length header

I'm having below code to send a multipart/form-data request.
List<Attachment> multipartData = new ArrayList<>();
ContentDisposition cd1 = new ContentDisposition("form-data; name=\"file\";
filename="+fileObj.getName());
FileInputStream inputStream = new FileInputStream(fileObj);
multipartData.add(new Attachment("file",inputStream, cd1));
MultipartBody multipart = new MultipartBody(multipartData);
In my RestClient class, I'm using the below lines of code to send a POST request using JAX-RS Client object
if ("POST".equals(method)) {
response = this.client.getBuilder().post(Entity.entity(entity,MediaType.MULTIPART_FORM_DATA));
I checked the HTTP request body using Wiremock and is as below:
Transfer-Encoding: [chunked]
Accept: [*/*]
Cache-Control: [no-cache]
User-Agent: [Apache-CXF/3.2.5]
Connection: [keep-alive]
Host: [127.0.0.1:9990]
Pragma: [no-cache]
Content-Type: [multipart/form-data; boundary="uuid:04b491f5-50de-4f4f-b7c0-cd745136d3d1"]
--uuid:04b491f5-50de-4f4f-b7c0-cd745136d3d1
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <file>
Content-Disposition: form-data; name="file"; filename=sample.txt
<File content goes here>
I want to know how the content-length header is missing in the request payload. Is there any way to set the content-length header to the request?
Please help me.
I used the apache cxf WebClient to unset the transfer encoding as chunked.
if ("POST".equals(method)) {
Invocation.Builder builder = this.client.getBuilder();
WebClient.getConfig(builder).getHttpConduit().getClient().setAllowChunking(false);
response = builder.post(Entity.entity(entity,MediaType.MULTIPART_FORM_DATA));
}
With this, the client is able to send the request with content-length header.

How to make $batch POST request using Olingo v2 and Java

I am trying to do a $batch request in Java using OData v2.
An example request from the browser would be something like below between the double quotes.
But how can I make this request programatically? Is there a sample call somewhere? Any help is appreciated.
Request URL: https://someUrl/project/odata/project/FOLDER/$batch
Request Method: POST
Status Code: 202 Accepted
Remote Address: 1.2.3.4:1234
Referrer Policy: no-referrer-when-downgrade
content-encoding: gzip
content-length: 5256
content-type: multipart/mixed; boundary=E828EB257B134AC6F567C8D3B67E666E1
dataserviceversion: 2.0
Accept: multipart/mixed
Accept-Encoding: gzip, deflate, br
Accept-Language: en
Connection: keep-alive
Content-Length: 595
Content-Type: multipart/mixed;boundary=batch_4edb-a2cd-948d
Cookie: project-usercontext=project-language=EN&project-client=100;
--Some cookie content--
DataServiceVersion: 2.0
Host: host.myClient.com:1234
MaxDataServiceVersion: 2.0
Origin: https://host.myClient.com:1234
Referer: https://host.myClient.com:1234/project/index.html
project-cancel-on-close: true
project-contextid-accept: header
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/1.2.3.4 Safari/537.36
x-csrf-token: 8Fd53yy2vuCjnaFKrZNuLg==
--batch_4edb-a2cd-948d
Content-Type: application/http
Content-Transfer-Encoding: binary
GET MyEntityDetailsSet HTTP/1.1
project-contextid-accept: header
Accept: application/json
Accept-Language: en
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
project-cancel-on-close: true
> --batch_4edb-a2cd-948d
Content-Type: application/http
Content-Transfer-Encoding: binary
GET MyObjectSet HTTP/1.1
project-contextid-accept: header
Accept: application/json
Accept-Language: en
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
project-cancel-on-close: true
--batch_4edb-a2cd-948d--
You can use Olingo V2 as an OData client (although a rather ugly one in my opinion). There is a full tutorial dedicated to this usage on the official Olingo site: How to use Apache Olingo as client library.
Olingo knows to build requests and parse responses, but you need an underlying mechanism to execute the HTTP calls. My recommendation would be to not rely on manually opening HttpURLConnections like in the above example, but to use something like Apache Http Client or some other dedicated library instead (in order to reduce the amount of code you write and also to have access to more advanced concepts like connection polling).
In a nutshell, you must first read and parse the metadata of the service that you want to consume:
// content = read the metadata as an InputStream
Edm dataModel = EntityProvider.readMetadata(content, false);
You can build a batch request via a fluent-style API:
BatchQueryPart part = BatchQueryPart.method("GET")
.uri("/Employees('1')")
.build();
// here you could have a larger list of parts, not just a singleton list
InputStream payload = EntityProvider.writeBatchRequest(
Collections.singletonList(part), "batch_boundary");
Then you have to just execute it using your HTTP request execution mechanism of choice (method = "POST" and body = the payload variable). Afterwards, you can parse the obtained response using Olingo:
// body = the response body received
// contentType = the Content-Type header received
List<BatchSingleResponse> responses =
EntityProvider.parseBatchResponse(responseBody, contentType);
// you can obtain the body for each request from the response list
String partBody = responses.get(0).getBody();
InputStream partStream = new ByteArrayInputStream(partBody.getBytes());
String partType = responses.get(0).getHeader(HttpHeaders.CONTENT_TYPE);
Lastly, using the Edm from the first step you can also parse each individual body based on the type of request that you build. For example you could use the readEntry method to de-serialize a single entity read:
// first we have to find the entity set you used to make the request
EdmEntitySet entitySet = edm.getDefaultEntityContainer()
.getEntitySet("Employees");
ODataEntry entry = EntityProvider.readEntry(partType, entitySet,
partStream, EntityProviderReadProperties.init().build())
Lastly, you can use the entry methods to get e.g. the properties.

Resteasy always get application/json content type on request

I have a web application that use Angularjs on frontend och Resteasy + Jackson on the back end. I'm sending a file from Angular component to a REST method, receiving method looks like this :
#POST
#Path("/upload/attachment/for/{eventId}")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(MultipartFormDataInput input,
final #PathParam("eventId") Long eventId,
#Context HttpServletRequest request) {
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
...my awesome stuff...
return Response.ok().build();
}
And request has following headers when sent :
Accept application/json, text/plain, */*
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Content-Length 347085
Content-Type multipart/form-data; boundary=---------------------------12164806981346771846716776342
Cookie JSESSIONID=aoBd1hgzR3GM8bSG5P-9g-vQ; csrftoken=ziQ7kN7TlMehR2aURDrmaMLYAroMsSpu
Host localhost:9000
Referer http://localhost:9000/local/myapp/index.html
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:32.0) Gecko/20100101 Firefox/32.0
The problem is that THIS request ALWAYS has application/json as a Content-Type instead for multipart/form-data as it says in the headers. And I get :
20:12:00,490 WARN [org.jboss.resteasy.core.SynchronousDispatcher] (http-/127.0.0.1:8080-2) Failed executing POST /events/upload/attachment/for/null: org.jboss.resteasy.spi.UnsupportedMediaTypeException: Cannot consume content type
Already in HttpServletDispatcher the content is wrong.
I can't get if this is JBOSS that set Content-Type to wrong value or some thing else.
Ok, it was that I have an extra layer between angular and server which runs on Node.js and it just tunel requests to the server. I had to add following :
var r = null;
r = request.put({uri: url, json: inReq.body, headers: inReq.headers, form: inReq.form});
inReq.pipe(r).pipe(inRes);
This set all the headers from outgoing request and tunnel the request
Now I have right content-type

Jersey Client set content-type header as text/plain

Using jersey client sending HTTP request. Content-Type header is automatically set as "application/json" (as a nature), but i want to changing "content-type" header with "text/plain" regardless of any specification, standards etc. Jersey version is 2.4.1.
Code
String target = "http://192.168.1.2:10000";
String path = "test3";
Client c = ClientBuilder.newClient ();
WebTarget target = c.target (target).path (path);
Entity<SubscriberBean> json = Entity.json (subscriber);
Builder request = target.request ();
String response = request.post(json, String.class);
Request
POST /test3 HTTP/1.1
Accept: text/html
Content-Type: application/json
User-Agent: Jersey/2.4.1 (HttpUrlConnection 1.6.0_17)
Host: 192.168.1.2:10000
Connection: keep-alive
Content-Length: 278
///**** Some json data ***///
instead of
request.post(json, String.class);
try to use
request.type(MediaType.TEXT_PLAIN).post(json, String.class);
Use Entity.text(entityData) or Entity.entity(entityData, mediaType) methods instead of Entity.json() in your example.

Categories