将授权标头添加到Xamarin Forms Android WebView中的所有请求

时间:2018-02-04 08:50:07

标签: xamarin.forms android-webview

我正在尝试将自定义http标头添加到webview客户端(用于授权)。 它似乎在某些情况下工作,我能够登录到网页而无需输入用户名和密码,我被重定向到另一个页面。但是当页面调用其他资源来获取填充数据的元素时,会抛出错误并调用OnReceivedHttpError。我得到的错误是未经授权的401,当我浏览IWebResourceRequest上的标题时,根本看不到授权标题。 我错过了什么或有没有人有同样的问题?

使用Xamarin Forms 2.3.3.180并定位API 21(Android 5.0 Lollipop),使用Android 7.1 Nougat进行编译。

我已经尝试在邮递员中添加标题以供请求,并且它可以完美运行。

渲染器:

public class MyWebViewRenderer : WebViewRenderer
{
    private MyWebViewClient _webViewClient;

    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
    {
        base.OnElementChanged(e);

        if(_webViewClient == null)
            _webViewClient = new MyWebViewClient();

        Control.SetWebViewClient(_webViewClient);
        Control.LongClickable = false;
        Control.HapticFeedbackEnabled = false;
        Control.Settings.JavaScriptEnabled = true;

        var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
        var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);

        var headers = new Dictionary<string, string>();
        headers.Add("Authorization", $"Basic {base64string}")

        Control.LoadUrl(Control.Url, headers);
    }
}

WebViewClient:

public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
    WebView.SetWebContentsDebuggingEnabled(true);

    var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
    var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);

    var headers = new Dictionary<string, string>();
    headers.Add("Authorization", $"Basic {base64string}")

    view.LoadUrl(url, headers);
    return true;
}

public override WebResourceResponse ShouldInterceptRequest(WebView view, IWebResourceRequest urlResource)
{
    //headers does not always contains authorization header, so let's add it.
    if (!urlResource.RequestHeaders.ContainsKey("authorization") && !urlResource.RequestHeaders.ContainsKey("Authorization"))
    {
       var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
       var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);

       urlResource.RequestHeaders.Add("Authorization", $"{base64string}");
     }

        return base.ShouldInterceptRequest(view, urlResource);
}

public override void OnReceivedHttpError(WebView view, IWebResourceRequest request, WebResourceResponse errorResponse)
{
    base.OnReceivedHttpError(view, request, errorResponse);
}

1 个答案:

答案 0 :(得分:0)

如果只需要get请求的标头,则下面的代码将起作用。但是POST请求是一个不同的问题。我需要做类似的事情(使用所有请求,而不仅仅是GET),我只能说没有简单的解决方案,至少不是我找到的解决方案(而且我尝试了所有不编写自己的解决方案的尝试)网络驱动程序)。我已经尝试了很多方法(ShouldOverrideUrlLoading,ShouldInterceptRequest,自定义LoadUrl和PostUrl等),但它们都没有给出100%的解决方案。有关此的错误信息很多,所以我认为需要澄清一下,因为我已经花了两天时间才取得成功。

这就是我学到的东西:

如果您只需要GET请求中的标头,那将是微不足道的。只需创建WebViewClient的实现并像这样覆盖ShouldOverrideUrlLoading

[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(App.Android.HybridWebViewRenderer))]
namespace App.Android
{
    public class HybridWebViewRenderer : WebViewRenderer
    {

        public HybridWebViewRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);
            Control.SetWebViewClient(new CustomWebViewClient());
        }
    }
    public class CustomWebViewClient : WebViewClient
    {
        public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
        {
            Dictionary<string, string> headers = new Dictionary<string, string>
            {
                ["Name"] = "value"
            };
            view.LoadUrl(url, headers);
            return true;
        }
    }
}

但是,如果您在其他请求(特别是POST请求)中需要标头,则确实不是一个完美的解决方案。许多答案告诉您覆盖ShouldInterceptRequest,但这不太可能帮助您。 ShouldInterceptRequest提供了一个IWebResourceRequest,其中包含请求的URL,方法(即POST)和标头。那里有答案,指出通过执行request.Headers.Add("Name", "Value")添加标题是可行的解决方案,但这是错误IWebResourceRequest的内部逻辑未使用WebView,因此对其进行修改是没有用的!

您可以在ShouldInterceptRequest中编写自己的HTTP客户端,其中包括您自己的标头,以执行请求并返回WebResourceResponse对象。同样,这适用于GET请求,但问题在于,即使我们可以拦截POST请求,我们也无法确定请求中的内容,因为请求内容未包含在{{ 1}}对象。结果,我们无法准确地手动执行请求。因此,除非POST请求的内容不重要或无法以某种方式获取,否则此方法不可行。

有关此方法的其他说明:返回null告诉IWebResourceRequest处理对我们的请求。换句话说,“我不想拦截请求”。但是,如果返回值不为null,则WebView将显示WebView对象中的所有内容。

我还尝试覆盖WebResourceResponse本身中的PostUrlLoadUrl方法。 内部逻辑不会调用这些方法,因此,除非您自己调用它们,否则此方法将无效。

那该怎么办?有一些hacky解决方案(请参阅github.com/KeejOow/android-post-webview)来解决此问题,但是它们依赖于javascript,因此并不适合所有情况(我已经读到它们不适用于表单) )。如果要在Xamarin中使用它们,则无论如何都需要修改C#的代码,并且不能保证它将解决您的问题。

我正在写这篇文章,所以没有其他人可以浪费无数的时间来寻找一个并不存在的解决方案。 如果只有Android开发人员决定将POST内容包括在WebView对象中…… 对于这个长度,我们深表歉意,如果您已经读到这一点,那么您可能和我一样绝望。