我正在使用Xamarin.iOS开发iOS应用,并且已经实现了WKNavigationDelegate的DecidePolicy(如果我的网络视图如下):
public override void DecidePolicy(WKWebView webView, WKNavigationResponse navigationResponse, Action<WKNavigationResponsePolicy> decisionHandler)
{
if (webView.Url.AbsoluteString == @"www.myurl.com")
{
var response = navigationResponse.Response as NSHttpUrlResponse;
System.Diagnostics.Debug.WriteLine(response.ToString());
}
decisionHandler?.Invoke(WKNavigationResponsePolicy.Allow);
}
此页面上有一个按钮,当单击该按钮时,它将称为“ POST”表单。依次提交表单并返回HTTP Status 200以及仅响应表单主体中的隐藏元素。我想检索这些隐藏的元素,以便在我的应用中进行进一步处理。
我的问题有两个部分:
我实际上不知道从上方使用覆盖的DecidePolicy(..)时是否已在该URL上提交表单?我可以使用其他DecidePolicy来实现:
public override void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy>decisionHandler)
{
if (navigationAction.NavigationType == WKNavigationType.FormSubmitted)
{
//perform logic
}
}
但是我不知道如何从这种方法的变体中检索响应主体。非常感谢您的帮助。
答案 0 :(得分:0)
我也刚要实现这个,我想我会发布它也许可以帮助其他人。
我的用例是我需要向某些请求注入一个 Authorization 标头,我设法使用相当标准的 WKNavigationDelegate 实现来实现。为此,我将取消现有请求,创建一个副本并注入我更改的标头,然后在保留 cookie 和现有连接属性的同一浏览器中重新提交请求。然而,有些调用失败了,我后来发现 POST 没有返回任何正文数据。
这就是我今天的工作方式;
public override async void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler)
{
if (ShouldInjectAuthorizationHeader(navigationAction.Request))
{
decisionHandler(WKNavigationActionPolicy.Cancel);
webView.LoadRequest(await CreateRequestWithAuthorisationHeader(webView, navigationAction.Request));
}
else
{
decisionHandler(WKNavigationActionPolicy.Allow);
}
}
这只是为了封装我正在检查是否需要修改调用的决策策略的逻辑。
ShouldInjectAuthorizationHeader 上面只是检查 request.Url 是否存在于已知 url 列表中,然后我检查现有标头是否已存在授权。
private async Task<NSUrlRequest> CreateRequestWithAuthorisationHeader(WKWebView webView, NSUrlRequest request)
{
var newRequest = (NSMutableUrlRequest)request.MutableCopy();
newRequest.ShouldHandleCookies = request.ShouldHandleCookies;
var requestModified = false;
//This is not needed usually but i recheck here that i have an accesstoken to use and that the url requires it
if (string.IsNullOrEmpty(AccessToken) == false && WebEx.DoesUrlMatchAnyPartial(request.Url.ToString(), InterceptUrls))
{
// inject the HTTP header
var headers = request.Headers == null ? new NSMutableDictionary() : new NSMutableDictionary(request.Headers);
headers.Add(new NSString("Authorization"), new NSString($"Bearer {AccessToken}"));
newRequest.Headers = headers;
if(request.HttpMethod == "POST")
{
try
{
//WKWebview has a 5 year running bug Apple https://bugs.webkit.org/show_bug.cgi?id=140188
//that the POST form data is not returned and not available so we run some custom javascript to serialise and expose it
//then manually copy it over and set up the correct headers for it
//Serilise form library from https://code.google.com/archive/p/form-serialize/
var javascript = @"
function serialize(form){if(!form||form.nodeName!==""FORM""){return }var i,j,q=[];for(i=form.elements.length-1;i>=0;i=i-1){if(form.elements[i].name===""""){continue}switch(form.elements[i].nodeName){case""INPUT"":switch(form.elements[i].type){case""text"":case""hidden"":case""password"":case""button"":case""reset"":case""submit"":q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value));break;case""checkbox"":case""radio"":if(form.elements[i].checked){q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value))}break;case""file"":break}break;case""TEXTAREA"":q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value));break;case""SELECT"":switch(form.elements[i].type){case""select-one"":q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value));break;case""select-multiple"":for(j=form.elements[i].options.length-1;j>=0;j=j-1){if(form.elements[i].options[j].selected){q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].options[j].value))}}break}break;case""BUTTON"":switch(form.elements[i].type){case""reset"":case""submit"":case""button"":q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value));break}break}}return q.join(""&"")};
serialize(document.querySelector('form'));";
var result = await webView.EvaluateJavaScriptAsync(javascript);
var data = result?.ToString();
if (string.IsNullOrWhiteSpace(data) == false)
{
newRequest.Body = data;
newRequest["Content-Length"] = $"{newRequest.Body.Length}";
newRequest["Content-Type"] = "application/x-www-form-urlencoded";
}
}
catch(NSErrorException ex)
{
Log.Error(ex, $"WKWebViewNavigationDelegate.CreateRequestWithAuthorisationHeader: JavaScript evaluation exception when trying to get Form post data, {ex.Message}");
}
}
requestModified = true;
}
if (requestModified)
{
return newRequest;
}
return request;
}
我为这篇文章稍微简化了上述两种方法,删除调试输出并清理变量,但大多数情况下它在我们的系统中运行
<块引用>看起来堆栈溢出不喜欢双引号缩小的js if 您想下载它并从 minified js for extracting form data 重新插入它,我将它放入文本编辑器并用“”替换所有“以允许它在@””字符串中内联解析
如果你不需要授权承载的东西,它会减少很多代码
基本上,我将 WKWebView 传递给该方法并在其中运行 javascript 以在其 POST 时提取表单数据,然后将其复制到新请求,用
取消旧请求decisionHandler(WKNavigationActionPolicy.Cancel);
并使用 LoadRequest 将 webview 重定向到原始的新 MutableCopy,并带有额外的标头和手动插入的 POST 数据
webView.LoadRequest(await CreateRequestWithAuthorisationHeader(webView, navigationAction.Request));
这似乎现在工作正常
对于您的要求 John,您应该能够非常轻松地使用现有代码获取数据
public override void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy>decisionHandler)
{
if (navigationAction.NavigationType == WKNavigationType.FormSubmitted)
{
//perform logic
var javascript = @"
function serialize(form){if(!form||form.nodeName!==""FORM""){return }var i,j,q=[];for(i=form.elements.length-1;i>=0;i=i-1){if(form.elements[i].name===""""){continue}switch(form.elements[i].nodeName){case""INPUT"":switch(form.elements[i].type){case""text"":case""hidden"":case""password"":case""button"":case""reset"":case""submit"":q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value));break;case""checkbox"":case""radio"":if(form.elements[i].checked){q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value))}break;case""file"":break}break;case""TEXTAREA"":q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value));break;case""SELECT"":switch(form.elements[i].type){case""select-one"":q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value));break;case""select-multiple"":for(j=form.elements[i].options.length-1;j>=0;j=j-1){if(form.elements[i].options[j].selected){q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].options[j].value))}}break}break;case""BUTTON"":switch(form.elements[i].type){case""reset"":case""submit"":case""button"":q.push(form.elements[i].name+""=""+encodeURIComponent(form.elements[i].value));break}break}}return q.join(""&"")};
serialize(document.querySelector('form'));";
var result = await webView.EvaluateJavaScriptAsync(javascript);
var data = result?.ToString();
if (string.IsNullOrWhiteSpace(data) == false)
{
/*
url encoded form data in data
You could replace the & with something else if you want it in a different format or try different javascript method, I found a few but I was after Url encoded so didn't experiment further, there were some alternatives that allowed for json format instead of url format
*/
}
}
}