Trying to post to a web view in Android. I need to pass in headers (username, password, contentType) AND body parameters. Currently I see webView.LoadUrl takes in the url and headers however I do not see any options for loading a url with body and headers. Just for reference Im trying to achieve something similar to this, which works perfectly in iOS:
public void OpenWebPage(ReviewWebRequest request)
{
var nsRequest = new NSMutableUrlRequest
{
Url = new NSUrl(request.Url),
HttpMethod = request.Method
};
var nsHeaders = request.Headers;
nsRequest.Headers = NSDictionary.FromObjectsAndKeys(nsHeaders.Values.ToArray(), nsHeaders.Keys.ToArray());
nsRequest.Body = NSData.FromString(request.BodyString, NSStringEncoding.UTF8);
webView.LoadRequest(nsRequest);
}
Related
In my Xamarin Forms app I have a very basic GET request that results in a 504 'Method not allowed' on Android.
This is the controller that I am calling:
[AllowAnonymous]
[HttpGet]
[Route("api/system/backendversion")]
public int GetBackendVersion()
{
return 20200924;
}
This is the code that performs the request
var _client = new HttpClient();
var content = new StringContent(json, Encoding.UTF8, "application/json");
var httpRequest = new HttpRequestMessage(httpMethod, url)
{
Content = content,
Version = HttpVersion.Version10
};
var response = await _client.SendAsync(httpRequest);
The problem disappears when I change the HttpClient implementation from "Android" to "Managed".
Also the webrequest works fine in the XF.UWP version of my app.
I believe I put it on Android for a reason, but I'm unsure what the reason was (probably speed). I'm curious what goes wrong here.
Apperantly it breaks because the content (header?) is set to json when there is no json.
I fixed it like this:
var httpRequest = new HttpRequestMessage(httpMethod, url)
{
Version = HttpVersion.Version10
};
if (!string.IsNullOrWhiteSpace(json))
{
httpRequest.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
var response = await _client.SendAsync(httpRequest);
this error is similar to iOS where you get an error if you put json body in a get request
if (httpMethod == HttpMethod.Get && !string.IsNullOrWhiteSpace(json))
{
Debugger.Break();//a get request with a body is not allowed on iOS and results in a error https://stackoverflow.com/questions/56955595/1103-error-domain-nsurlerrordomain-code-1103-resource-exceeds-maximum-size-i
}
I'm trying to append the cors headers to a request made in an Android webview by a JavaScript fetch command in order to bypass cors. To do this I'm intercepting requests made in the WebView with the shouldInterceptRequest member function.
The code I have seems to be working except that the stream I'm getting from the HttpURLConnection doesn't seem to work with the stream required by the WebResourceResponse. The relevant code (inside shouldInterceptRequest function):
val url = URL("https://www.android.com")
val urlConnection: HttpURLConnection = url.openConnection() as HttpURLConnection
try {
var headers =
urlConnection.headerFields.mapValues { it.value.joinToString() }
headers = headers.toMutableMap()
headers["Access-Control-Allow-Origin"] = "*"
headers["Access-Control-Allow-Headers"] = "*"
headers["Access-Control-Allow-Credentials"] = "true"
// val inStream: InputStream = BufferedInputStream(urlConnection.inputStream) // Doesn't work
// val string = "hello world :)"
// val inStream = string.byteInputStream(charset = Charset.forName("UTF-8")) // Works when used as stream, sends "hello world :)" as response
return WebResourceResponse(
headers["Content-Type"],
if (urlConnection.contentEncoding === null) "UTF-8" else urlConnection.contentEncoding,
urlConnection.responseCode,
"OK",
headers,
urlConnection.inputStream
)
} finally {
urlConnection.disconnect()
}
The last argument of the WebResourceResponse needs to be a stream which will be sent to the Webview page. When using the urlConnection.inputStream I can see in Chrome devtools that the headers are set properly on the response, but the data fails to load.
When I replace the response stream with a string the solution works fine, and the webview receives the string correctly. My question is then why doesn't the stream from the HttpURLConnection work when given to the response object, and how do I make it work.
The solution I'm looking for should not only support text, but also binary data and streams.
As a side note I've also tried implementing the requestIntercept asynchronously, as mentioned here: How to get WebViewClient.shouldInterceptRequest invoked asynchronously, which changes the stream given to the response object, however this resulted in a very slow request followed by an android crash after the data had loaded and the request was done.
This isn't a duplicate of WebResourceResponse can't read full inputstream from HttpConnection (Android), the problem is different and the solution wouldn't work.
Entire code file: https://gist.github.com/RuurdBijlsma/2b52d80a4d74460ac2837ee55b0b933c in case that's helpful.
I have made a webviewclient to intercept the request and adding the header and call loadUrl but unsure it works yet.
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
{
var headers = new System.Collections.Generic.Dictionary<string, string>();
headers.Add("Authorization", "bearer " + "s2vIKZd_P91MaaxaZ_XyeUpph6wQNrQ81pcQdUXjFTB3r48RaU9");
view.LoadUrl(request.Url.ToString(), headers );
return true;
}
I can load the page with DefaultHttpClient but it was deprecated.
Your code should work fine and solve your problem, but if you want to load the page with DefaultHttpClient, maybe you want to override the ShouldInterceptRequest method in order to intercept every request.
DefaultHttpClient is deprecated, we can use HttpClient instead, to use this, we need to install the System.Net.Http package in Xamarin.Android project. Then you can for example code like this:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("URL");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
var httprequest = new HttpRequestMessage(HttpMethod.Post, "relativeAddress");
client.DefaultRequestHeaders.Add("Authorization", "bearer " + "s2vIKZd_P91MaaxaZ_XyeUpph6wQNrQ81pcQdUXjFTB3r48RaU9");
var httpresponse = client.SendAsync(httprequest).Result;
}
I have a webapi that i want to post json to and then return json. Im using xamarin to create my android app but it doesn't seem to support PostAsJsonAsync method for the httpclient. So im now trying PostAsync method that post httpcontent. So what i want to do is convert my json to so that it is of format httpcontent and json so that i can post it to my webapi. This is my code:
var clientRequest = new ResourceByNameRequest
{
Name = "G60",
UserId = "1"
};
var param = JsonConvert.SerializeObject(clientRequest);
HttpContent content = new StringContent(param, Encoding.UTF8, "application/json");
var client = new HttpClient();
var cancellationToken = new CancellationToken();
var result = client.PostAsync("https://mctwebapi-test.entrematic.com/api/Resource/ResourceByName?", content, cancellationToken).Result;
return reslist;
this just runs till the timeout. I can't figure out why it doesn't work. If you have any other suggestions on how to post json to webapi using Xamarin im more than happy to try that out!
Plz help!
We developed a .NET web service for mobile application several years ago. This service is being called by iPhone/Android/Blackberry/WindowsPhone and all native apps developed by a third party. We added support for JSON, so some apps access this service using JSON calls, and some using SOAP.
The webservice returns JSON only when the request is sent with HTTP header Content-type: application/json.
We encountered a problem with one Android platform (specifically the Galaxy Nexus), where the Content-Type header is missing for GET requests. Our third party app developer tried many solutions but could not find a way to force send the Content-Type for GET requests.
However, we did notice that the Accept header is set correctly and sent, but I found no way to change the web service to use that header instead of Content-Type to return JSON in those cases.
Here is the example request, which results with XML response, and not JSON as needed.
GET /mobile/service.asmx/Logon?system=2&username='test'&password='1234' HTTP/1.1
Accept: application/json
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
Connection: Keep-Alive
And excerpt from the webservice code:
[WebMethod(
BufferResponse = false,
CacheDuration = 0
)]
[ScriptMethod(UseHttpGet = true,ResponseFormat = ResponseFormat.Json) ]
public LogonResponse Logon(int system, string username, string password)
{
return service.Logon(system, username, password);
}
Is there a way to force JSON response in some way, or inspecting the Accept header to do so? (Other than migrating to WCF?)
If not, I was told by the app developer they use the Spring framework to make HTTP requests. If there's a solution on how to make it work on the app side and force send the Content-Type header for GET requests, it's also appreciated!
Thanks!
You could try this (unable to test at the moment).
public LogonResponse Logon(int system, string username, string password)
{
string accept = HttpContext.Current.Request.Headers("Accept");
if (!string.IsNullOrEmpty(accept)) {
if (accept.ToLower.EndsWith("application/json")) {
HttpContext.Current.Response.ContentType = "application/json";
}
}
return service.Logon(system, username, password);
}
Edit: Updated the Request to response
Thanks for the reply.
Although setting the content-type did not solve the issue, it did point me in the right direction.
The following solved it for me:
[WebMethod(
BufferResponse = false,
CacheDuration = 0
)]
[ScriptMethod(UseHttpGet = true,ResponseFormat = ResponseFormat.Json) ]
public LogonResponse Logon(int system, string username, string password)
{
return SetOutput<LogonResponse>(service.Identify(username, password));
}
and the actual conversion:
public static T SetOutput<T>(object response)
{
var accept = HttpContext.Current.Request.Headers["Accept"];
var ctype = HttpContext.Current.Request.ContentType;
if (string.IsNullOrEmpty(ctype) && !string.IsNullOrEmpty(accept))
if (accept.ToLower().EndsWith("application/json"))
{
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(response);
HttpContext.Current.Response.ContentType = "application/json";
HttpContext.Current.Response.Write(json);
HttpContext.Current.Response.End();
}
return (T)response;
}
Cheers!
Use the following to also wrap the whole thing with the expected "d" element:
public static T SetOutput<T>(T response)
{
var accept = HttpContext.Current.Request.Headers["Accept"];
var ctype = HttpContext.Current.Request.ContentType;
if (string.IsNullOrEmpty(ctype) && !string.IsNullOrEmpty(accept))
if (accept.ToLower().EndsWith("application/json"))
{
var wrapper = new JSONWrapper<T> {d = response};
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(wrapper);
HttpContext.Current.Response.ContentType = "application/json";
HttpContext.Current.Response.Write(json);
HttpContext.Current.Response.End();
}
return response;
}
public class JSONWrapper<T>
{
public T d { set; get; }
}