how to handle "Transfer-Encoding=chunked" in SI HttpRequestExecutingMessageHandler - spring

I am calling an external server using HttpRequestExecutingMessageHandler. I am using a JSON to object transformer to convert the JSON data. But I am getting the following exception.
Caused by: com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: (String)"�
when I checked the headers I found the following.
Transfer-Encoding=chunked
Is this the reason for the exception log?
The outbound gateway and JsonToObjectTransformer are given below:
#ServiceActivator(inputChannel = "channelOutboundRequest")
#Bean
public HttpRequestExecutingMessageHandler outboundGateway() {
final HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(
endpoint);
handler.setExpectedResponseType(String.class);
handler.setHttpMethod(HttpMethod.POST);
handler.setOutputChannelName("channelResponse");
handler.setAdviceChain(Collections.singletonList(advice()));
return handler;
}
#Bean
#Transformer(inputChannel = "channelResponse", outputChannel = "channelReply")
public JsonToObjectTransformer transformer(ObjectMapper objectMapper) {
final JsonObjectMapper<?, ?> mapper = new Jackson2JsonObjectMapper(objectMapper);
return new JsonToObjectTransformer(DetailsDTO.class, mapper);
}
If the header is causing the issue, how can I handle the response?
Note: If I hit the external server directly using postman, I am getting the response in JSON structure.
I have no idea what's wrong here.. If I use the simple restemplate call like below, it works properly.
JSONObject jsonObject = new JSONObject("{\"code\":\"F001\",\"transactionId\":\"1008566223232\"}");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth("token");
HttpEntity<String> request =
new HttpEntity<String>(jsonObject.toString(), headers);
String respns = restTemplt.postForObject("http://endpoint", request, String.class);
System.out.println(respns);
JSONObject response = new JSONObject(respns);
One difference I could find was in the response headers. those are given below:
Outbound gateway response headers :-
{Transfer-Encoding=chunked, http_requestMethod=GET, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#d8b195e, Server=nginx, Accept=/, Connection=keep-alive, User-Agent=PostmanRuntime/7.28.0, Host=localhost:8901, Accept-Encoding=gzip, deflate, br, http_statusCode=200 OK, Date=1622533072000, Authorization=Bearer token, replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#d8b195e, Cache-Control=no-cache, ETag=W/"1009-5SzdL+uWyY6ZcMWht5dMtm2Sxlc", Content-Encoding=gzip, http_requestUrl=http://inboundurl, id=be07fc8d-d478-5fa9-33e4-61a2b5f92468, Content-Length=207, contentType=application/json;charset=utf-8, Content-Type=application/json, requestFrom=CUSTOM_HEADER, timestamp=1622533092827}
Normal restTemplate call response header
[Server:"nginx", Date:"Tue, 01 Jun 2021 07:34:54 GMT", Content-Type:"application/json; charset=utf-8", Content-Length:"4105", Connection:"keep-alive", Access-Control-Allow-Origin:"*", Content-Security-Policy:"default-src 'self';base-uri 'self';block-all-mixed-content;font-src 'self' https: data:;frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests", X-DNS-Prefetch-Control:"off", Expect-CT:"max-age=0", X-Frame-Options:"SAMEORIGIN", Strict-Transport-Security:"max-age=15552000; includeSubDomains", X-Download-Options:"noopen", X-Content-Type-Options:"nosniff", X-Permitted-Cross-Domain-Policies:"none", Referrer-Policy:"no-referrer", X-XSS-Protection:"0", ETag:"W/"1009-llD9DqxYkEsjyikWajYk+16cb1k""]
Can anyone please help?

After many trials and errors, I found the reason for this. Accept-Encoding=gzip, deflate, br this header in the outbound gateway request is the root cause. the response I am getting is a long string and it is getting compressed because of this header. i added a header filter before the outbound gateway to remove this header.
#Bean
#Transformer(inputChannel = "channelHeaderFilterReq", outputChannel = "channelHeaderFilterRes")
public HeaderFilter filter() {
return new HeaderFilter("Accept-Encoding");
}
Now everything works fine..!!

Related

sending GET request via REST template with JSON request body getting failed with binding binding element must be a struct error?

I am trying to send a GET request using REST Template with a JSON request body, but the request is failing with error,
processing
failedorg.springframework.web.client.HttpServerErrorException$InternalServerError:
500 Internal Server Error: [code=400, message=binding element must be
a struct]
I have tried hitting the endpoint using the insomnia and the request is going through successfully, There I have put 2 headers
1. Content-Type - application/json
2. Authorization - Bearer ******
And the JSON body.
My code in spring boot looks like this.
ResponseEntity<String> responseObject = null;
String URL = "https://myurl/endpoint";
String requestBody = "{\"requestType\":\"status\"}";
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization","Bearer ***");
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity httpEntity = new HttpEntity<>(body,headers);
System.out.println(httpEntity+" httpEntity");
System.out.println(headers+" headers");
responseObject = restTemplate.exchange(URL, HttpMethod.GET, httpEntity, String.class);
The sout for httpentity and header looks like this
httpEntity
<{"requestType":"status"},[Authorization:"Bearer *******************", Content-Type:"application/json"]>
headers
[Authorization:"Bearer *************************", Content-Type:"application/json"]
Also when I am trying to send a request without the body to another endpoint using rest template, that is getting executed successfully, so I think something with the way I am sending the body has to do with the error.
rest template doesn't support get request with body . for more details you can refer this article.
If you are on Java 11 I would suggest you to use java.net.HttpClient which will fulfill your need.

How to remove and add new Accept header to Spring boot Rest Template?

I'm consuming a GraphQL Service, it is returning 406 Not Acceptable if the Accept header is text/plain.
It is expecting to Accept as application/json
So I tried override the RestTeamplate Headers. However it seems the Accept header test/plain is always present there. I confirmed this by enabling debug(logging.level.org.springframework.web.client.RestTemplate=DEBUG)
Console
o.s.web.client.RestTemplate: Accept=[text/plain, application/json, application/*+json, */*]
o.s.web.client.RestTemplate: Writing [{products(query: "title:tow*", first: 10) {edges {node {id legacyResourceId title}}}}] as "application/graphql"
Here is the code I tried to override the Accept header
HttpHeaders headers = new HttpHeaders();
RestTemplate restTemplate = new RestTemplate();
headers.add("Content-Type","application/graphql");
headers.setAccept(Collections.singletonList(new MediaType("application","json")));
String content = "{products(query: \"title:tow*\", first: 10) {edges {node {id title}}}}";
HttpEntity<String> requestEntity = new HttpEntity<String>(content, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
I even tried removing the Accept header first(headers.remove("Accept")) and then setAccept but still it doesn't remove it from the log.
Is there anything else need to be done?
Why is it not removing the text/plain from the Accept header?
Why I see multiple options in Accept when I set only one option?

Http 400 using resttemplate postforobject

Below is the code which gives 400 bad request, tried various options with this but always ends up with 400 bad request, however the same works using OkHttp client
HttpHeaders hh = new HttpHeaders();
RestTemplate rt = new RestTemplate();
String reqBody = new ObjectMapper.writeValueAsString(“test msg”);
headers.set(“Accept”, “text/plain”);
headers.set(“content-type”,”application/json”);
headers.set(“authorization”, “Basic xxxxx”);
headers.set(“ibm-mq-rest-csrf-token”,”blank”);
HttpEntity<String> request = new HttpEntity<>(reqBody.toString());
ResponseEntity<Object> result = rt.postForObject(url, request, Object.class);
Below code works using okhttp
Your content does not appear to be JSON, in both cases it appears to be plain text. Therefore the header
Content-Type: text/plain
should be used rather than
Content-Type: application/json
I am unsure why this works in your OKHTTP example, unless perhaps the media type you use to build your post body somehow over-rides your Content-Type header?

Invalid mimetype exception in Spring boot rest call

I am new to both Spring boot and rest calls.
I am trying to consume a rest service and I do not have any information about that rest API except URL. When I hit that URL from a browser I am getting a response as {key:value}. So, I assumed that it is a JSON response.
I am consuming it in spring boot as follows
restTemplate.getForObject(url, String.class) .
This is giving Invalid mime type "content-type: text/plain; charset=ISO-8859-1": Invalid token character ':' in token "content-type: text"
I assume that this error is because response content type is set to text/plain but it is returning JSON format.
EDIT:
Tried this way but did not work.
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>("parameters",headers);
ResponseEntity<String> result = restTemplate.exchange(url,HttpMethod.GET,
entity, String.class);
How to handle and solve it?
You might want to read about the request headers your REST API needs. Content-Type header specifies the media type of the request you're sending to the server. Because you're just getting data from the server you should set the Accept header to the kind of response you want i.e., Accept: application/json.
Unfortunately, you can't set headers using getForObject(). You could try this:
URL url = new URL("Enter the URL of the REST endpoint");
con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Accept", "application/json");
if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
StringBuffer content = new StringBuffer();
String inputLine;
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
}

ALM 12 REST using SpringFramework RestTemplate: "401 QCSession cookie missing"

In ALM 12 we have to explicity call "qcbin/rest/site-session" to get session.
When I GET call "/qcbin/rest/site-session" I receive the following:
"Set-Cookie=[BIGipServerALMAST330P-QC=rd100o00000000000000000000ffff0fe0dd74o8080; path=/, ]""
I extract the cookie as described here:
HP ALM 12 REST not returning QCSession cookie.
Instead of this RestConnector, our project is using RestTemplate from SpringFramework, so I did:
private HashMap getQCSession() throws Exception {
URL url = new URL("https://almxxxx.saas.hp.com/qcbin/rest/site-session?login-form-required=y");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/xml");
conn.setRequestProperty("Accept", "application/xml");
conn.connect();
HashMap cookies = updateCookies(conn);
return cookies;
}
public HashMap updateCookies(HttpURLConnection conn) {
String cookie2 = conn.getHeaderField("Set-Cookie");
int equalIndex = cookie2.indexOf('=');
int semicolonIndex = cookie2.indexOf(';');
String cookieKey = cookie2.substring(0, equalIndex);
String cookieValue = cookie2.substring(equalIndex + 1, semicolonIndex);
HashMap cookies = new HashMap();
cookies.put(cookieKey, cookieValue);
System.out.println(cookies.toString());
return cookies;
}
To send the cookie in the GET call using the RestTemplate I followed the instructions from http://springinpractice.com/2012/04/08/sending-cookies-with-resttemplate/, so I did:
public <U extends Object> ResponseEntity<U> getFromURL(URI url, Class<U> responseBodyClass) throws Exception {
logger.info("GET na URL: {} esperando classe: {} no response", url, responseBodyClass);
HashMap cookie = this.getQCSession();
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", this.getQCSession().toString());
this.httpEntity = new HttpEntity(null, requestHeaders);
return super.exchange(url, HttpMethod.GET, this.httpEntity, responseBodyClass);
}
The requestHeaders content added to the HttpEntity (SpringFramework) is:
{Cookie=[{BIGipServerALMAST330P-QC=rd100o00000000000000000000ffff0fe0dd74o8080}]}
However I'm still getting "401 QCSession cookie missing".
I already tried to send in the GET call the JSESSIONID, with no success as well.
I appreciate any help.
Any clue?
I ran into this. As of ALM12 you need to create a session also.
I POST some XML or JSON to here "/authentication-point/alm-authenticate" to authenticate
Then collect the Set-Cookie header
I then POST to "/rest/site-session" with the cookie from the previous response.
I collect the session cookies from that response to use in my subsequent requests.
Hope that helps
I don't know, if it can help you but you are sending it with query param for UI authentication.
"POST .../rest/site-session?login-form-required=y"
I would suggest to POST it without query param
"POST .../rest/site-session"
Also the order of actions you should do before asking for QCSession token is:
1.Check whether you are authenticated
"GET .../rest/is-authenticated"
2.If not you'll get reference where to authenticate: WWW-Authenticate: LWSSO realm=".../authentication-point"
3.Send basic auth header to authentication point with added alm-authenticate at the end. Which returns you LWSSO_COOKIE_KEY.
"POST .../authentication-point/alm-authenticate"
Authentication: Basic BASE64{username:password}
4.Then you'll need to POST that LWSSO_COOKIE_KEY to site-session and ALM will return you QCSession key.
"POST .../rest/site-session"
"Cookie: LWSSO_COOKIE_KEY={cookie}; Path=/"
Hopefully I was able to help you.
If you still have problems, feel free to contact me.

Resources