カスタムヘッダーをWebViewリソースリクエストに追加する-Android


95

WebViewからのすべてのリクエストにカスタムヘッダーを追加する必要があります。loadURLのパラメータがあることはわかっていますがextraHeaders、これらは最初のリクエストにのみ適用されます。後続のすべてのリクエストにはヘッダーが含まれていません。ですべてのオーバーライドを見てきましたがWebViewClient、リソース要求にヘッダーを追加することはできません- onLoadResource(WebView view, String url)。どんな助けでも素晴らしいでしょう。

ありがとう、レイ


2
@MediumOne:これは、あなたが欠けていると考える機能であるため、バグではありません。HTTP仕様では、後続のHTTPリクエストが以前のHTTPリクエストの任意のヘッダーをミラーリングする必要があると述べていることは知りません。
CommonsWare 2013年

1
@CommonsWare:「後続」という単語は、ここでは誤解を招く可能性があります。ブラウザで「facebook.com」と入力してfacebook.comホームページをロードすると、CSS、js、imgファイルをロードするためのサポートされている「リソース要求」がいくつかあります。これは、F12機能([ネットワーク]タブ)を使用してChromeで確認できます。これらのリクエストの場合、webviewはヘッダーを追加しません。addons.mozilla.org/en-us/firefox/addon/modify-headersプラグインを使用して、FireFoxリクエストにカスタムヘッダーを追加してました。このプラグインは、そのようなサポートしているすべての「リソース要求」にヘッダーを追加することができました。WebViewも同じようにする必要があると思います。
MediumOne、2013

1
@MediumOne:「WebViewも同じようにするべきだと思います」-これは、欠けていると思われる機能です。Firefoxでこれを行うには、プラグインを使用する必要があったことに注意してください。提案された機能が悪い考えだと言っているのではありません。私はそれをバグとして特徴付けても、この提案された機能をAndroidに追加するためのあなたの原因を助ける可能性は低いと言っています。
CommonsWare 2013

1
@CommonsWare:WebViewを使用して、カスタムHTTPプロキシで動作するように構成できるブラウザーを構築しているとします。このプロキシはカスタム認証を使用し、リクエストにはカスタムヘッダーが必要です。現在、webviewはカスタムヘッダーを設定するAPIを提供していますが、内部的には、生成するすべてのリソースリクエストにヘッダーを設定していません。これらのリクエストのヘッダーを設定するための追加のAPIもありません。したがって、WebViewリクエストへのカスタムヘッダーの追加に依存する機能は失敗します。
MediumOne、2013

1
@CommonsWare-私は4年後にこの会話を再訪します。私は今同意します-これはバグではありません。HTTP仕様には、後続のリクエストが同じヘッダーを送信する必要があるとの記述はありません。:)
MediumOne

回答:


80

試す

loadUrl(String url, Map<String, String> extraHeaders)

リソース読み込みリクエストにヘッダーを追加するには、カスタムWebViewClientを作成してオーバーライドします。

API 24+:
WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)
or
WebResourceResponse shouldInterceptRequest(WebView view, String url)

9
申し訳ありませんが、これは機能しません。ヘッダーも最初のリクエストにのみ適用されます。ヘッダーはリソース要求に追加されません。他のアイデア?ありがとう。
レイ

19
はい、次のようにWebClient.shouldOverrideUrlLoadingをオーバーライドします。public boolean shouldOverrideUrlLoading(WebView view、String url){view.loadUrl(url、extraHeaders); trueを返します。}
peceps

5
@peceps-リソースのロード中にコールバック「shouldOverrideUrlLoading」が呼び出されない。たとえば、を試すとview.loadUrl("http://www.facebook.com", extraHeaders)'http://static.fb.com/images/logo.png'webiewから送信されるetcなどの複数のリソース要求があります。これらのリクエストの場合、追加のヘッダーは追加されません。そして、そのようなリソース要求の間、shouldOverrideUrlLoadingは呼び出されません。コールバック 'OnLoadResource'が呼び出されますが、この時点でヘッダーを設定する方法はありません。
MediumOne 2013年

2
@MediumOne、リソースの読み込みについては、WebViewClient.shouldInterceptRequest(android.webkit.WebView view, java.lang.String url)チェックアウトAPIをオーバーライドしてください。
ヨーク2013

3
@yorkw:このメソッドはすべてのリソース要求URLをキャプチャします。ただし、これらのリクエストにヘッダーを追加する方法はありません。私の目標は、すべてのリクエストにカスタムHTTPヘッダーを追加することです。shouldInterceptRequestメソッドを使用してこれを達成できる場合、どのように説明できますか?
MediumOne、2013

36

WebViewClient.shouldInterceptRequestを使用して各リクエストをインターセプトする必要があります

インターセプトごとに、URLを取得し、このリクエストを自分で作成して、コンテンツストリームを返す必要があります。

WebViewClient wvc = new WebViewClient() {
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {

        try {
            DefaultHttpClient client = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("MY-CUSTOM-HEADER", "header value");
            httpGet.setHeader(HttpHeaders.USER_AGENT, "custom user-agent");
            HttpResponse httpReponse = client.execute(httpGet);

            Header contentType = httpReponse.getEntity().getContentType();
            Header encoding = httpReponse.getEntity().getContentEncoding();
            InputStream responseInputStream = httpReponse.getEntity().getContent();

            String contentTypeValue = null;
            String encodingValue = null;
            if (contentType != null) {
                contentTypeValue = contentType.getValue();
            }
            if (encoding != null) {
                encodingValue = encoding.getValue();
            }
            return new WebResourceResponse(contentTypeValue, encodingValue, responseInputStream);
        } catch (ClientProtocolException e) {
            //return null to tell WebView we failed to fetch it WebView should try again.
            return null;
        } catch (IOException e) {
             //return null to tell WebView we failed to fetch it WebView should try again.
            return null;
        }
    }
}

Webview wv = new WebView(this);
wv.setWebViewClient(wvc);

最小APIターゲットがレベル21の場合、URLだけでなく、追加のリクエスト情報(ヘッダーなど)を提供する新しいshouldInterceptRequestを使用できます。


2
このトリックを使用しているときに誰かが私と同じ状況に遭遇した場合に備えて。(とにかくこれは良いものです。)ここにあなたへの注意があります。charsetなどのオプションのパラメーターを含めることができるhttp content-typeヘッダーは、WebResourceResponseコンストラクターの最初のパラメーターの要件であるMIMEタイプと完全に互換性がないため、何らかの方法でcontent-typeからMIMEタイプの部分を抽出する必要があります。 RegExpなど、ほとんどの場合に機能するように考えることができます。
James Chen

2
このイベントは非推奨です。public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)代わりにこちらを
Hirdesh Vishwdewa

3
@HirdeshVishwdewa-最後の文を見てください。
Martin Konecny、2015年

2
スーパービューのshouldInterceptRequestメソッドの結果をWebビューと変更されたリクエストをパラメーターとして返すことで、独自のロードをスキップできます。これは、URLに基​​づいてトリガーし、リロード時にURLを変更せず、無限ループに陥るシナリオで特に便利です。新しいリクエストの例にも感謝します。Javaで処理する方法は、私には直感に反しています。
Erik Reppen、2016年

4
HttpClientは、compileSdk 23との上方には使用できません
タマシュKozmér

30

多分私の応答はかなり遅くなりますが、それは21レベル以下と21レベル以上の APIをカバーしています。

ヘッダーを追加するには、すべてのリクエストインターセプトし、必要なヘッダーを持つ新しいリクエスト作成する必要があります。

したがって、両方のケースで呼び出されるshouldInterceptRequestメソッドをオーバーライドする必要があります。1 .レベル21までのAPI、2。2. APIレベル21以上の場合

    webView.setWebViewClient(new WebViewClient() {

        // Handle API until level 21
        @SuppressWarnings("deprecation")
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {

            return getNewResponse(url);
        }

        // Handle API 21+
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

            String url = request.getUrl().toString();

            return getNewResponse(url);
        }

        private WebResourceResponse getNewResponse(String url) {

            try {
                OkHttpClient httpClient = new OkHttpClient();

                Request request = new Request.Builder()
                        .url(url.trim())
                        .addHeader("Authorization", "YOU_AUTH_KEY") // Example header
                        .addHeader("api-key", "YOUR_API_KEY") // Example header
                        .build();

                Response response = httpClient.newCall(request).execute();

                return new WebResourceResponse(
                        null,
                        response.header("content-encoding", "utf-8"),
                        response.body().byteStream()
                );

            } catch (Exception e) {
                return null;
            }

        }
   });

応答タイプを処理する必要がある場合は、変更できます

        return new WebResourceResponse(
                null, // <- Change here
                response.header("content-encoding", "utf-8"),
                response.body().byteStream()
        );

        return new WebResourceResponse(
                getMimeType(url), // <- Change here
                response.header("content-encoding", "utf-8"),
                response.body().byteStream()
        );

そしてメソッドを追加

        private String getMimeType(String url) {
            String type = null;
            String extension = MimeTypeMap.getFileExtensionFromUrl(url);

            if (extension != null) {

                switch (extension) {
                    case "js":
                        return "text/javascript";
                    case "woff":
                        return "application/font-woff";
                    case "woff2":
                        return "application/font-woff2";
                    case "ttf":
                        return "application/x-font-ttf";
                    case "eot":
                        return "application/vnd.ms-fontobject";
                    case "svg":
                        return "image/svg+xml";
                }

                type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
            }

            return type;
        }

1
この古い投稿に回答して申し訳ありませんが、このコードを使用すると、私のアプリはページをロードする代わりにファイルをダウンロードしようとします(失敗します)。
Giacomo M

どうもありがとうございました!
AlexS

21

前に述べたように、これを行うことができます:

 WebView  host = (WebView)this.findViewById(R.id.webView);
 String url = "<yoururladdress>";

 Map <String, String> extraHeaders = new HashMap<String, String>();
 extraHeaders.put("Authorization","Bearer"); 
 host.loadUrl(url,extraHeaders);

これをMVCコントローラーでテストし、Authorize属性を拡張してヘッダーを検査しました。ヘッダーはそこにあります。


Kit-Katで機能し、作成および投稿されたときと同じように、これに対処する必要があります。Lolly Popを試したことがありません。
leeroya 2016

ジェリービーンまたはマシュマロで私のために働いていません...ヘッダーの何も変更しません
Erik Verboom

6
これは、OPが要求することを行いません。彼は、webviewによって行われたすべてのリクエストにヘッダーを追加したいと考えています。これにより、最初のリクエストのみにカスタムヘッダーが追加されます
NinjaCoder 2017

これはOPが要求するものではありません
Akshay

これはOPが探していたものに答えないことを知っていますが、これはまさに私が望んだものでした。つまり、WebViewIntent URLに追加のヘッダーを追加します。感謝します!
ジョシュアピンター

9

これは私にとってはうまくいきます:

  1. まず、メソッドを作成する必要があります。これは、リクエストに追加するヘッダーを返します。

    private Map<String, String> getCustomHeaders()
    {
        Map<String, String> headers = new HashMap<>();
        headers.put("YOURHEADER", "VALUE");
        return headers;
    }
  2. 次に、WebViewClientを作成する必要があります。

    private WebViewClient getWebViewClient()
    {
    
        return new WebViewClient()
        {
    
        @Override
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
        {
            view.loadUrl(request.getUrl().toString(), getCustomHeaders());
            return true;
        }
    
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url)
        {
            view.loadUrl(url, getCustomHeaders());
            return true;
        }
    };
    }
  3. WebViewClientをWebViewに追加します。

    webView.setWebViewClient(getWebViewClient());

お役に立てれば。


1
見栄えは良いですが、これはヘッダーを追加しますか、それともヘッダーを置き換えますか?
イヴォレンケマ2017

@IvoRenkema loadUrl(String url, Map<String, String> additionalHttpHeaders) は、追加ヘッダーを追加することを意味します
AbhinayMe

4

loadUrlをスキップして、JavaのHttpURLConnectionを使用して独自のloadPageを書き込むことにより、すべてのヘッダーを制御できるはずです。次に、WebビューのloadDataを使用して応答を表示します。

Googleが提供するヘッダーにはアクセスできません。それらはWebViewソースの深いJNI呼び出しにあります。


1
あなたの答えであなたが言ったことへの言及はありますか?あなたの答えで実装の参照を与えることは他の人にとって役立つでしょう。
Hirdesh Vishwdewa

1

以下はHttpUrlConnectionを使用した実装です。

class CustomWebviewClient : WebViewClient() {
    private val charsetPattern = Pattern.compile(".*?charset=(.*?)(;.*)?$")

    override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
        try {
            val connection: HttpURLConnection = URL(request.url.toString()).openConnection() as HttpURLConnection
            connection.requestMethod = request.method
            for ((key, value) in request.requestHeaders) {
                connection.addRequestProperty(key, value)
            }

            connection.addRequestProperty("custom header key", "custom header value")

            var contentType: String? = connection.contentType
            var charset: String? = null
            if (contentType != null) {
                // some content types may include charset => strip; e. g. "application/json; charset=utf-8"
                val contentTypeTokenizer = StringTokenizer(contentType, ";")
                val tokenizedContentType = contentTypeTokenizer.nextToken()

                var capturedCharset: String? = connection.contentEncoding
                if (capturedCharset == null) {
                    val charsetMatcher = charsetPattern.matcher(contentType)
                    if (charsetMatcher.find() && charsetMatcher.groupCount() > 0) {
                        capturedCharset = charsetMatcher.group(1)
                    }
                }
                if (capturedCharset != null && !capturedCharset.isEmpty()) {
                    charset = capturedCharset
                }

                contentType = tokenizedContentType
            }

            val status = connection.responseCode
            var inputStream = if (status == HttpURLConnection.HTTP_OK) {
                connection.inputStream
            } else {
                // error stream can sometimes be null even if status is different from HTTP_OK
                // (e. g. in case of 404)
                connection.errorStream ?: connection.inputStream
            }
            val headers = connection.headerFields
            val contentEncodings = headers.get("Content-Encoding")
            if (contentEncodings != null) {
                for (header in contentEncodings) {
                    if (header.equals("gzip", true)) {
                        inputStream = GZIPInputStream(inputStream)
                        break
                    }
                }
            }
            return WebResourceResponse(contentType, charset, status, connection.responseMessage, convertConnectionResponseToSingleValueMap(connection.headerFields), inputStream)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return super.shouldInterceptRequest(view, request)
    }

    private fun convertConnectionResponseToSingleValueMap(headerFields: Map<String, List<String>>): Map<String, String> {
        val headers = HashMap<String, String>()
        for ((key, value) in headerFields) {
            when {
                value.size == 1 -> headers[key] = value[0]
                value.isEmpty() -> headers[key] = ""
                else -> {
                    val builder = StringBuilder(value[0])
                    val separator = "; "
                    for (i in 1 until value.size) {
                        builder.append(separator)
                        builder.append(value[i])
                    }
                    headers[key] = builder.toString()
                }
            }
        }
        return headers
    }
}

WebResourceRequestはPOSTデータを提供しないため、これはPOSTリクエストでは機能しないことに注意してください。POSTデータのインターセプトにJavaScriptインジェクションの回避策を使用するリクエストデータ-WebViewClientライブラリがあります。


0

これでうまくいきました。以下のようにWebViewClientを作成し、WebクライアントをWebビューに設定します。(コンテンツ内の)URLにはbaseurlがなく、相対URLしかなかったため、webview.loadDataWithBaseURLを使用する必要がありました。loadDataWithBaseURLを使用して設定されたbaseurlがある場合にのみ、URLを正しく取得します。

public WebViewClient getWebViewClientWithCustomHeader(){
    return new WebViewClient() {
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
            try {
                OkHttpClient httpClient = new OkHttpClient();
                com.squareup.okhttp.Request request = new com.squareup.okhttp.Request.Builder()
                        .url(url.trim())
                        .addHeader("<your-custom-header-name>", "<your-custom-header-value>")
                        .build();
                com.squareup.okhttp.Response response = httpClient.newCall(request).execute();

                return new WebResourceResponse(
                        response.header("content-type", response.body().contentType().type()), // You can set something other as default content-type
                        response.header("content-encoding", "utf-8"),  // Again, you can set another encoding as default
                        response.body().byteStream()
                );
            } catch (ClientProtocolException e) {
                //return null to tell WebView we failed to fetch it WebView should try again.
                return null;
            } catch (IOException e) {
                //return null to tell WebView we failed to fetch it WebView should try again.
                return null;
            }
        }
    };

}

私にとってはうまくいきます:.post(reqbody)where RequestBody reqbody = RequestBody.create(null、 "");
Karoly

-2

あなたはこれを使うことができます:

@Override

 public boolean shouldOverrideUrlLoading(WebView view, String url) {

                // Here put your code
                Map<String, String> map = new HashMap<String, String>();
                map.put("Content-Type","application/json");
                view.loadUrl(url, map);
                return false;

            }

2
これは単にURLをリロードし続けますね?
Onheiron、2016年

-3

同じ問題に遭遇して解決しました。

前に述べたように、カスタムWebViewClientを作成し、shouldInterceptRequestメソッドをオーバーライドする必要があります。

WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)

そのメソッドは、「空の」WebResourceResponseを返すときにwebView.loadUrlを発行する必要があります。

このようなもの:

@Override
public boolean shouldInterceptRequest(WebView view, WebResourceRequest request) {

    // Check for "recursive request" (are yor header set?)
    if (request.getRequestHeaders().containsKey("Your Header"))
        return null;

    // Add here your headers (could be good to import original request header here!!!)
    Map<String, String> customHeaders = new HashMap<String, String>();
    customHeaders.put("Your Header","Your Header Value");
    view.loadUrl(url, customHeaders);

    return new WebResourceResponse("", "", null);
}

このメソッドからview.loadUrlを呼び出すと、アプリがクラッシュするようです
willcwf 2018年

@willcwfこのクラッシュの例はありますか?
Francesco

@Francesco私のアプリもクラッシュします
Giacomo M

クラッシュは役に立たないと言って、あまりにも多くがこれに反対票を投じています。より具体的に、エラー情報を記述してください。
フランチェスコ

-14

これを使って:

webView.getSettings().setUserAgentString("User-Agent");

11
これは質問の答えにはなりません
younes0

これは認証ヘッダーと同じではありません
Vlad
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.