Showing POST Response in a WebView Control
In the world of connected applications programmatic HTTP requests are more and more common. In Windows Store applications HttpClient
class serves most purposes as long as communication is targeted at services or at least the results are processed programmatically and don't need to be shown directly to the user:
var http = new HttpClient();
// GET request
var getRequest = "http://hroch486.icpf.cas.cz/cgi-bin/echo.pl?key=value";
var getResponse = await http.GetAsync(getRequest);
var getResult = await getResponse.Content.ReadAsStringAsync();
// POST request
var postUri = new Uri("http://hroch486.icpf.cas.cz/cgi-bin/echo.pl");
var postFields = new Dictionary<string, string>
{
{ "key", "value" }
};
var postContent = new FormUrlEncodedContent(postFields);
var postResponse = await http.PostAsync(postUri, postContent);
var postResult = await postResponse.Content.ReadAsStringAsync();
There are cases, though, when its necessary to display the response to the user unchanged in a web browser, e.g. when displaying search results he will use as a starting point for further navigation. The simplest approach would be to just display the string response as HTML in a WebView
control:
MyWebView.NavigateToString(postResult);
Unfortunately this doesn't work as expected in all but the simplest cases. As soon as there are any relative links included in the page or external files referenced, the page won't work correctly. The browser needs to be aware of the actual page URI. For GET
requests there is built in support in WebView
control:
MyWebView.Navigate(new Uri(getRequest));
POST
requests are a tougher nut to crack. There's no method available to directly invoke it on a WebView
control, therefore we need to find another approach. Since the POST
request needs to be sent from the WebView
context, InvokeScript
is a good candidate as long as we can ensure a Javascript method invoking the required request. This means making the POST
request will be a two step process:
- Display a web page containing custom HTML and Javascript required for the
POST
request in theWebView
control. - Automatically invoke the
POST
request by calling the Javascript method.
We can wrap all that in a WebView
extension method to make it easier to use:
public static void PostAndNavigate(this WebView webView,
string requestUri, IEnumerable<KeyValuePair<string, string>> formFields)
{
var formElements = String.Join("",
formFields.Select(
p => String.Format(@"<input type=""hidden"" value=""{0}"" name=""{1}"">",
p.Value, p.Key)));
var html = String.Format(
@"<html>
<head>
<script type=""text/javascript"">
function Submit()
{{
document.getElementById('pay').submit();
}}
</script>
</head>
<body>
<form id=""pay"" method=""POST"" action=""{0}"">
{1}
</form>
</body>
</html>", requestUri, formElements);
LoadCompletedEventHandler loadCompletedDelegate = null;
loadCompletedDelegate = (s, e) =>
{
webView.LoadCompleted -= loadCompletedDelegate;
webView.InvokeScript("Submit", null);
};
webView.NavigateToString(html);
webView.LoadCompleted += loadCompletedDelegate;
}
There's an additional complication in the above code because InvokeScript
doesn't find the Javascript method if it is called immediately after NavigateToString
. The web page must first complete loading for the call to succeed, therefore an event handler is required.