Spring WebClient POST and Content Length Header for application/x-www-form-urlencoded - spring-boot

I am using Spring WebClient and Spring Boot 2.3.5.RELEASE to POST a request to a site that wants a Content Type of application/x-www-form-urlencoded. It keeps failing because the Content-Length header is not set. I can set that in the code, but I'm not sure how to compute the Content-Length when the Content-Type is application/x-www-form-urlencoded. The URL I am accessing is a legacy site. I saw this post Missing Content-Length header sending POST request with WebClient (SpringBoot 2.0.2.RELEASE) but it doesn't address the issue for Content-Type=application/x-www-form-urlencoded.
I executed the same request in Postman, and it works fine. In Postman if I remove the Content-Length header I get the same error I see in the code.
Please advise how to compute the Content-Length. Thank you.
Here is a snippet.
final MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("key 1, "value 1");
formData.add("key 2, "value 2");
formData.add("key 3, "value 3");
formData.add("key 4, "value 4");
ResponseEntity<String> resp = webClient
.post()
.uri("https://myurl")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header(HttpHeaders.CONTENT_LENGTH,
String.valueOf(???))
.body(BodyInserters.fromFormData(formData))
.exchange()
.flatMap(response -> response.toEntity(String.class))
.block();

I have a solution. Turns out that this had nothing to do with Content-Length. I decided to try accessing the same REST endpoint with RestTemplate. I got an identical error where the target endpoint complained about missing parameters. Yet, when I accessed it with Postman or curl it worked perfectly fine.
Instead of using BodyInserters.fromFormData with a MultiValueMap, I used BodyInserters.fromValue(bodyData). For the body data I built a String of values similar to what curl used.
grant_type=client_credentials&client_id=123&client_secret=456&scope=myScope
This issue is perhaps limited to the host serving this API(not a public one), but I found it interesting that RestTemplate and WebClient both failed with a standard approach for x-www-form-urlencoded, while Postman and curl worked just fine.

Related

How to send request data in post request using zuul filter

I am new to Zuul and I want to redirect to external url using zuul pre filter in spring boot application. I want to send request data to call external url (post method api in python).
When i try to send request data using below code, when i hit the url from postman,i get 405 The method is not allowed for the requested URL.
my code:
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
InputStream in = request.getInputStream();
String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
ctx.set(body);
when i hit the url like http://localhost:8080/customers/ passing request body in postman,it should redirect to external url (api method) by sending post data but i am getting getting 405 The method is not allowed for the requested URL.
Could any one please help me in solving this issue? I want to send request data in zuul and call external api. Thank you

POST request with Content-Type application/x-www-form-urlencoded and

My requirement is very simple.
Call POST request with id and password.
Header has Content-Type = application/x-www-form-urlencoded
and data is also passed as urlencoded like below
Response is coming in xml format.
I tried a lot of examples from everywhere but nothing seems to be working. It gives me back 401 Unauthorized which is an error that target API throws if request is not in proper format.
http://zetcode.com/java/getpostrequest/
Exactly what I needed.
Java HTTP POST request with HttpURLConnection section on the page did the work.

RestTemplate postForObject return HttpClientErrorException: 401 null

I am trying to send a post message to authentication server and I am usign postforobject. When I run the program, i am getting "Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 401 null" error
RestTemplate rest = new RestTemplate();
String responseEntity;
String uri = "http://...";
responseEntity = rest.postForObject(uri, null, String.class);
System.out.println(responseEntity);
Also I tried
responseEntity = rest.postForObject(uri, "", String.class);
Same result, it didn't work.
When I using Simple Rest Client it works
enter image description here
Any idea?
401 null probably means that the server expected some form of authentication but none was presented (i.e. no Authorization header in your request)
The most likely explanation is: the Simple REST Client (re)uses Chrome's cookies. If you previously authenticated against your service using a form login, the Simple Rest Client will reuse the JSESSIONID cookie. To detect that simply press F12 and open the network tab of your debugger. FYI there are other browser plugins, for example "Advanced REST Client", which give you more visibility over what's being sent to the server.
When you send a request using restTemplate, it doesn't have access to the session cookie (which is to be expected). You should either post to URI that allows anonymous access, or provide some form of authentication in the request. If you require additional help please post your security configuration.

Cannot put parameters in body for OAuth2 POST requests in a REST service

It seems I am missing something very basic here.
I made a REST Api that takes POST requests for generating tokens using the Apache Oltu OAuth2 service, that looks something like this :
#POST
#Consumes("application/x-www-form-urlencoded")
#Produces("application/json")
public Response authorize(#Context HttpServletRequest request) throws OAuthSystemException, IOException {
try {
OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
When I use HttpRequester or Postman to test the service, it works perfectly fine on condition that I input all authentication and OAuth2 parameters as input parameters, as an example :
https://localhost:8443/rest/OAuthService/token?grant_type=password&username=userfortest&password=Johhny1é&client_id=1234
However I read, that for any POST requests, all parameters should be
in the Body of the HTTP request and never sent through with the url as a simple parameter. When I try to pass it in the body of the HTTP request, so as to make the request secure (so the url is the same without parameters and all params are specified in the body), it seems like it doesn't receive anything from the body as it throws an exception, after
OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
with the following message :
{"error_description":"Missing grant_type parameter value","error":"invalid_request"}
Is it the intended behaviour of Oltu/OAuth2 for the parameters to be passed through with the url? Or what am I doing wrong?
Thanks in advance.
Your answer is here: Unable to retrive post data using ,#Context HttpServletRequest when passed to OAuthTokenRequest using Oltu
I did exactly what he said and it worked perfectly.
You need modify Response authorize() parameters.

How to POST to a RESTful API on an ESP8266 using authentication?

I can enter this URL from a browser, and after entering my credentials this successfully calls my API http://172.16.0.40/rest/vars/set/1/12/666.
I'm trying to do this from an an ESP8266 using HTTPClient. My credentials are username:password, and I used an online conversion utility to get dXNlcm5hbWU6cGFzc3dvcmQ=.
When executed, the following returns error 701 (no idea what that is).
HTTPClient http;
http.begin("172.16.0.40", 80, "/");
http.addHeader("Content-Type", "text/plain");
http.addHeader("Authorization", "dXNlcm5hbWU6cGFzc3dvcmQ=");
auto httpCode = http.POST("rest/vars/set/1/12/999");
If I comment out the Authorization header, I get a 401, which is unauthorized access. What am I doing wrong?
You are trying to do a POST request to http://172.16.0.40/ with rest/vars/set/1/12/999 as payload.
HTTP status code 701 is not a standard code and is probably server specific.
You probably meant to do:
HTTPClient http;
http.begin("172.16.0.40", 80, "/rest/vars/set/1/12/999");
http.addHeader("Content-Type", "text/plain");
http.addHeader("Authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ=");
auto httpCode = http.POST(payload);
If you want a GET request, call http.GET() instead of http.POST(payload) and you should get the same response as inside the browser.
Edit:
And as #MaximilianGerhardt already answered, you need to prepend Basic to your Authorization header.
The header of such an authorization must look like (Wikipedia):
Authorization: Basic d2lraTpwZWRpYQ==
In short, you're probably just missing the "Basic" part. And need do change your code to
http.addHeader("Authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ=");

Resources