setEntity equivalent in OkHttp - Android - java

I'm migrating from the Apache HTTP legacy client to OkHttp and I'm having some problems finding equivalences between both. A couple of days ago I asked about credentials in this same subject and now I'm stuck again:
In the old implementation I have this:
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
reqGen.setCertReq(true);
MessageDigest digest = MessageDigest.getInstance("SHA256");
digest.update(myData);
TimeStampRequest request = reqGen.generate(TSPAlgorithms.SHA256, digest.digest(), BigInteger.valueOf(100));
byte[] enc_req = request.getEncoded();
myHttpPost.setEntity(new ByteArrayEntity(enc_req));
The most relevant line is the last one (as the others just build the request and, lucky enough, I won't need to change them), which adds the entity to the HttpPost.
Checking this answer it seems the entity of a request is
the majority of an HTTP request or response, consisting of some of the headers and the body, if present. It seems to be the entire request or response without the request or status line
But this definition confuses me as I can't find the equivalence to something with "headers and the body" in OkHttp. What I've tried:
MediaType textPlain = MediaType.parse("text/plain; charset=utf-8");
RequestBody requestBody = RequestBody.create(textPlain, request.getEncoded().toString());
Request myNewRequest = (new Request.Builder()).url(urlString).post(requestBody).build();
But it didn't work (I'm getting a 500 from the server). Does anyone know the correct equivalence?

I finally found the answer: I can use the TimeStampRequest encoded as I did before, without any modification. The change is, as I thought, only for the setEntity.
This is the request using OkHttp:
MediaType textPlain = MediaType.parse("binary");
RequestBody requestBody = RequestBody.create(textPlain, request.getEncoded());
Request myNewRequest = (new Request.Builder()).url(urlString).post(requestBody).build;
As you can see the only change from the previous code I tried is that I use binary as the MediaType, which make sense as we are sending a byte array (previously used ByteArrayEntity from the Apache client).
Hope it helps somebody.

Related

Grails encoded params Post Request

I am trying to complete the Instagram Oauth flow,
I currently have the authorization code which I'm to exchange for the access token. I am to make an x-www-form-urlencoded POST request to this endpoint
"https://api.instagram.com/oauth/access_token?"
This is what I've done so far.
String query = "https://api.instagram.com/oauth/access_token/?client_id=" + clientId +"&client_secret="+ clientSecret+ "&grant_type=authorization_code&redirect_uri="+ redirectUri + "&code=" + code
String response = new URL(query).getText()
A JSON string is expected as response.
Please Keep in mind that I'm a beginner.
I haven't read the Instagram documentation but based on your example code there's a couple of things to keep in mind:
you mentioned that you have to make a POST request, your example makes a GET request
never build a URL with untrusted parameter values. This basically means: always encode parameters, never trust them.
There are dozens of 3rd party HTTP Request libraries that give you flexibility and easier insight into aspects like timeouts and redirects. Java 11 has a built-in HTTP client that might ease this as well. But building on your code provided in your question using basic Java connection primitives this might work:
URL url = new URL("https://api.instagram.com/oauth/access_token/?client_id=${URLEncoder.encode(clientId, 'UTF-8')}&client_secret=${URLEncoder.encode(clientSecret, 'UTF-8')}&grant_type=authorization_code&redirect_uri=${URLEncoder.encode(redirectUri, 'UTF-8')}&code=${URLEncoder.encode(code, 'UTF-8')}")
def jsonString = ((HttpURLConnection) url.openConnection()).with {
setRequestMethod('POST')
setRequestProperty('Accept', 'application/json')
setDoInput(true)
connect()
if (getResponseCode() >= 400)
throw new Exception("Error code = ${getResponseCode()}")
inputStream.text
}
Every URL parameter is encoded so that any non-URL safe characters they contain are made safe, then we tell the connection that it will be a 'POST' and that we expect to get back json as input. inputStream.text is groovy code that takes an inputstream from the connection and reads all of the contents and then closes the stream. Since it is the last line of the with closure it is automatically returned as the value of the closure and assigned to the variable jsonString.

How to send a SAML Request?

i want to send a SAML request to my IDP (Azure AD) but ia m not sure how to send the request at all.
First i used OpenSAML to build an AuthRequest. Which i encoded as a String.
Now i wanted to use ApacheHttpClient to send the request and read the response and i am not sure if OpenSAML provides http sending methods at all so my idea was to use Apaches HttpClient for this for now.
String encodedAuthRequest = generateAuthRequest();
String url = "http://myidp/samlendpoint";
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(url);
// add request header
request.addHeader("User-Agent", USER_AGENT);
// what is to add else?
HttpResponse response = client.execute(request);
I am stuck now since i am not sure how to setup the request, does it need to be a query parameter like ?saml=.... in GET or do i have to put the encoded saml response in the body as POST..
Can someone help or clarify these issue?
Update from Guillaumes answer:
I have this from the IDPs MetaData:
<IDPSSODescriptor>
<SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://myidp/saml2" />
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://myidp/saml2" />
Depends on which binding you are supposed to use. The IdP documentation or metadata should mention that. There are several:
Redirect Binding (using a GET), by far the most common for Requests
POST Binding
Artifact Binding (more complex, but I have never seen it used for Requests)
...
I suppose that Redirect Binding will be used in your case (EDIT: you added the metadata from your IdP, it mentions that you can use both Redirect and POST bindings). It is described here: https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf page 15.
Short version: your must first use the DEFLATE algorithm to compress your XML Request, encode it using base64, encode it using URL encoding, then pass it as a query parameter named SAMLRequest
?SAMLRequest=<your url-encoded base64-encoded deflated authnrequest>
https://en.wikipedia.org/wiki/SAML_2.0#SP_Redirect_Request.3B_IdP_POST_Response

How can I make a put request with okhttp3 without multipart body

My web service, which is a java servlet, accepts a put request with url params to take a specific action. The code below gets what I want done, but I would love to know if there is a better way to make a put request without adding a multipart body.
Do all put requests in Java Servlets expect a multipart body?
Do all put requests made using okhttp3 expect a multipart body?
Am I misunderstanding something else?
body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("", "") // I would love to eliminate this.
.build();
request = new Request.Builder()
.url(url + "?my_param=" + URLEncoder.encode(myParam, "utf-8"))
.put(body)
.build();
response = client.newCall(request).execute();
From a pragmatic point of view, I'd say that you're looking at an implementation restriction of Servlets and/or OkHttp and it can probably be made to work using different libraries.
However,
from a standards view, I think your approach is incorrect and you should use a POST instead of a PUT. This requires reading both RFC-2616 (HTTP/1.1), section 9.6 on the POST request, and RFC-1630 (URL's in WWW), the section on query strings.
From the HTTP spec, section 9.6:
The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.
From the URL's spec, page 6:
QUERY STRINGS
The question mark ("?", ASCII 3F hex) is used to delimit the boundary between the URI of a queryable object, and a set of words used to express a query on that object. When this form is used, the combined URI stands for the object which results from the query being applied to the original object.
These two combine to imply that you cannot use a PUT request in the way that you're trying to.

Spring (for Android) and Jackson2: PUT as application/x-www-form-urlencoded

I am somewhat new to HTTP REST operations on Android, and a server I am working with uses PUT commands to process updates. I am having a difficult time using Spring (for Android) with Jackson2. The server doesn't seem to work with application/json put requests (though it will reply with them), and only seem to work with application/x-www-form-urlencoded versions (tested with python and curl. On python, if I set the header type to application/json, it fails.
I am using the latest versions of Spring and Jackson2, and I know everything is setup properly because my get request on the same URL gets me all the correct information.
I am using Robospice, but I don't really think that is relevant. Here is my request code.
#Override
public GPIO loadDataFromNetwork() throws Exception {
String url = String.format("http://%s/api/control/gpio", ip);
RestTemplate rt = getRestTemplate();
DefaultHttpClient client = new DefaultHttpClient();
Credentials defaultcreds = new UsernamePasswordCredentials("admin",
password);
client.getCredentialsProvider().setCredentials(
new AuthScope(routerip, 80, AuthScope.ANY_REALM), defaultcreds);
// Makes authentication work.
rt.setRequestFactory(new HttpComponentsClientHttpRequestFactory(client));
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity request = new HttpEntity(data, requestHeaders);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter map = new MappingJackson2HttpMessageConverter();
messageConverters.add(map);
messageConverters.add(new FormHttpMessageConverter());
rt.setMessageConverters(messageConverters);
ResponseEntity<GPIO> r = rt.exchange(url, HttpMethod.PUT, request, GPIO.class);
return r.getBody();
}
I am getting the exception stating it cannot find a way to convert:
02-01 10:59:29.466: E//DefaultRequestRunner.java:138(30086):
10:59:29.474 Thread-11651 An exception occurred during request network
execution :Could not write request: no suitable HttpMessageConverter
found for request type [com.xxxxx.control.gpio.GPIO] and content
type [application/x-www-form-urlencoded]
GPIO is my POJO object. I want to 'put' that to the server, as in serialize and put it.
I have looked at the following question that seems fairly relevant:
Deserializing Nested objects using RestTemplate
However, I need the result of my put command, and that requires me to use exchange() because Spring's put() returns nothing.
I have tried several different items (such as removing GPIO references, setting specific headres...) and none seem to work. I have a feeling this is probably an easy solution that I don't know how to fix. If anyone can help me that would be great.
TLDR: I'm using Spring for Android with Jackson2. I want to serialize my object (in this example, GPIO) so I can do a PUT request with the content type application/x-www-form-urlencoded. However I cannot get jackson to convert to that type, only to application/json, which does not work for me. I am not sure how to fix this, and I have run out of ideas. If I can't find a solution I'll probably have to dumb robospice. (or jackson, not sure which yet.)
Solution
Spring for Android doesn't seem to simplify things, so I dumped it and used the apache client directly in my loadDataFromNetwork() method. Robospice handles it pretty well and I can get the responses I need. If you are new to HTTP like I was take the time and learn the apache client, it's far easier in my opinion. Tweaking the ObjectMapper (like making a JsonTree and parsing that) made it much easier to get the data I needed without having to do as much work with POJO objects.
If you can format the data you want to send into MultiValueMap<String, String>, then a possible way around this is to use FormHttpMessageConverter.
FormHttpMessageConverter: (you can see examples in the link)
An HttpMessageConverter implementation that can read and write form
data from the HTTP request and response. By default, this converter
reads and writes the media type application/x-www-form-urlencoded.
Form data is read from and written into a MultiValueMap<String,String>.
After re-reading, here's a shot at a real answer - you are explicitly using the x-www-form-urlencoded content type by using this RequestHeader:
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
You should be using APPLICATION_JSON - Here's the Javadoc.
You should also consider specifying the charset and datatype in the headers. Jaxson is very specific about this, and if you don't have access to the server code you don't know what headers they expect.
dude i am using Loopj's AsyncHttpClient for rest and json dataset.
Here is the link below Here
Very simple and easy to understand. U can try this thing.

Content Type header for POST request

I have in Java (similar in other languages, problem should be language independent) a POST request I am sending to the server. The POST request contains only some POST parameters no body.
I basically have this:
postData = URLEncoder.encode("user", "UTF-8") + "=" + URLEncoder.encode("jackychan", "UTF-8");
//HttpSessionToken.setRequestProperty("Content-Type", "application/xml");
OutputStream postContent = (OutputStream)HttpSessionToken.getOutputStream();
postContent.write(postData.getBytes("UTF-8"));
This works fine. The question is around the second line, a comment at the moment. Uncommenting this line ruins my code, okay my data is not XML so I can understand this. To some REST services you have to POST a whole XML document, but no POST parameters, something like this
postData = "<xml> whatever xml structure here </xml>"
HttpSessionToken.setRequestProperty("Content-Type", "application/xml");
OutputStream postContent = (OutputStream)HttpSessionToken.getOutputStream();
postContent.write(postData.getBytes("UTF-8"));
This works too. The difference is the postData is now an XML and the content type is set.
The question now is, what if a Service requires BOTH, POST parameters as in example 1 AND an xml body as in example 2. How would I do this? If that never happens, why doesn't it happen?
Thanks, A.
You could do that as multipart/form-data so you can have mixed content in a single POST body. It's similar to multipart-mime, and each part can have its own content-type. Here's a previous stackoverflow answer for multipart form-data in Java: How can I make a multipart/form-data POST request using Java?

Categories

Resources