Fügen Sie benutzerdefinierte Header zu WebView-Ressourcenanforderungen hinzu - android

94

Ich muss JEDER Anfrage aus dem WebView benutzerdefinierte Header hinzufügen. Ich weiß, loadURLhat den Parameter für extraHeaders, aber diese werden nur auf die ursprüngliche Anfrage angewendet. Alle nachfolgenden Anforderungen enthalten nicht die Header. Ich habe mir alle Überschreibungen in angesehen WebViewClient, aber nichts erlaubt das Hinzufügen von Headern zu Ressourcenanforderungen - onLoadResource(WebView view, String url). Jede Hilfe wäre wunderbar.

Danke, Ray

Strahl
quelle
2
@MediumOne: Dies ist kein Fehler, sondern eine Funktion, die Sie für vermisst halten. Mir ist in der HTTP-Spezifikation nichts bekannt, was besagt, dass nachfolgende HTTP-Anforderungen beliebige Header aus früheren HTTP-Anforderungen spiegeln müssen.
CommonsWare
1
@ CommonsWare: Das Wort "nachfolgend" ist hier irreführend. Wenn ich in einem Browser " facebook.com " eingebe, um die facebook.com-Homepage zu laden, gibt es mehrere unterstützende "Ressourcenanforderungen" zum Laden der CSS-, js- und img-Dateien. Sie können dies in Chrome mithilfe der F12-Funktion (Registerkarte "Netzwerk") überprüfen. Für diese Anforderungen werden in der Webansicht keine Header hinzugefügt. Ich habe versucht , benutzerdefinierte Header zu FireFox Anfragen Hinzufügen Verwendung der addons.mozilla.org/en-us/firefox/addon/modify-headers Plug-in. Dieses Plugin konnte allen solchen unterstützenden "Ressourcenanforderungen" Header hinzufügen. Ich denke, WebView sollte das gleiche tun.
MediumOne
1
@MediumOne: "Ich denke, WebView sollte dasselbe tun" - eine Funktion, die Sie für vermisst halten. Beachten Sie, dass Sie auf ein Plugin zurückgreifen mussten, damit Firefox dies tut. Ich sage nicht, dass Ihre vorgeschlagene Funktion eine schlechte Idee ist. Ich sage, dass es unwahrscheinlich ist, dass es Ihrer Sache hilft, diese vorgeschlagene Funktion zu Android hinzuzufügen, wenn Sie sie als Fehler charakterisieren.
CommonsWare
1
@CommonsWare: Angenommen, ich verwende eine WebView, um einen Browser zu erstellen, der für die Verwendung mit einem benutzerdefinierten HTTP-Proxy konfiguriert werden kann. Dieser Proxy verwendet eine benutzerdefinierte Authentifizierung, bei der Anforderungen an ihn einen benutzerdefinierten Header haben sollten. Jetzt bietet Webview eine API zum Festlegen benutzerdefinierter Header. Intern wird der Header jedoch nicht auf alle von ihm generierten Ressourcenanforderungen festgelegt. Es gibt keine zusätzlichen APIs, um Header für diese Anforderungen festzulegen. Daher schlägt jede Funktion fehl, bei der benutzerdefinierte Header zu WebView-Anforderungen hinzugefügt werden müssen.
MediumOne
1
@ CommonsWare - Ich besuche dieses Gespräch nach 4 Jahren erneut. Ich stimme jetzt zu - das sollte kein Fehler sein. Die HTTP-Spezifikation enthält nichts, was besagt, dass nachfolgende Anforderungen dieselben Header senden sollten. :)
MediumOne

Antworten:

81

Versuchen

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

Erstellen Sie zum Hinzufügen von Headern zu Ressourcenladeanforderungen einen benutzerdefinierten WebViewClient und überschreiben Sie:

API 24+:
WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)
or
WebResourceResponse shouldInterceptRequest(WebView view, String url)
Peceps
quelle
8
Entschuldigung, aber das funktioniert nicht. Es werden nur die Header auch auf die anfänglichen Anforderungen angewendet. Header werden NICHT zu den Ressourcenanforderungen hinzugefügt. Andere Ideen? Vielen Dank.
Ray
19
Ja, überschreiben Sie WebClient.shouldOverrideUrlLoading wie folgt: public boolean shouldOverrideUrlLoading (WebView-Ansicht, String-URL) {view.loadUrl (url, extraHeaders); return true; }
Peceps
5
@peceps - Der Rückruf 'shouldOverrideUrlLoading' wird beim Laden der Ressource nicht aufgerufen. Wenn wir zum Beispiel versuchen view.loadUrl("http://www.facebook.com", extraHeaders), gibt es mehrere Ressourcenanforderungen wie'http://static.fb.com/images/logo.png' usw. von der Webansicht gesendet. Für diese Anforderungen werden die zusätzlichen Header nicht hinzugefügt. Und shouldOverrideUrlLoading wird bei solchen Ressourcenanforderungen nicht aufgerufen. Der Rückruf 'OnLoadResource' wird aufgerufen, es gibt jedoch derzeit keine Möglichkeit, Header festzulegen.
MediumOne
2
@MediumOne, zum Laden von Ressourcen, überschreiben. Weitere Informationen finden WebViewClient.shouldInterceptRequest(android.webkit.WebView view, java.lang.String url)Sie in der API .
Yorkw
3
@yorkw: Diese Methode erfasst alle URLs für Ressourcenanforderungen. Es gibt jedoch keine Möglichkeit, diesen Anforderungen Header hinzuzufügen. Mein Ziel ist es, allen Anforderungen benutzerdefinierte HTTP-Header hinzuzufügen. Wenn dies mit der shouldInterceptRequestMethode erreicht werden kann, können Sie bitte erklären, wie?
MediumOne
36

Sie müssen jede Anforderung mit abfangen WebViewClient.shouldInterceptRequest abfangen

Bei jedem Abfangen müssen Sie die URL nehmen, diese Anfrage selbst stellen und den Inhaltsstrom zurückgeben:

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);

Wenn Ihr Mindest-API-Ziel Stufe 21 ist , können Sie die neue shouldInterceptRequest verwenden, die Ihnen zusätzliche Anforderungsinformationen (z. B. Header) anstelle nur der URL bietet.

Martin Konecny
quelle
2
Nur für den Fall, dass jemand auf dieselbe Situation stößt, die ich bei diesem Trick habe. (Dies ist sowieso eine gute.) Hier ist eine Notiz für Sie. Da der Header des http-Inhaltstyps, der optionale Parameter wie Zeichensätze enthalten kann, nicht vollständig mit dem MIME-Typ kompatibel ist, ist die Anforderung des ersten Parameters des WebResourceResponse-Konstruktors erforderlich, sodass wir den MIME-Typteil auf jeden Fall aus dem Inhaltstyp extrahieren sollten Ich kann mir vorstellen, wie RegExp, damit es in den meisten Fällen funktioniert.
James Chen
2
Dieses Ereignis ist veraltet. Verwenden Sie public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)stattdessen mehr hier
Hirdesh Vishwdewa
3
@HirdeshVishwdewa - schau dir den letzten Satz an.
Martin Konecny
2
Sie können das Laden selbst überspringen, indem Sie die Ergebnisse der shouldInterceptRequest-Methode der Oberklasse mit Ihrer Webansicht und der geänderten Anforderung als Parameter zurückgeben. Dies ist besonders praktisch in Szenarien, in denen Sie basierend auf der URL auslösen, diese beim Neuladen nicht ändern und in eine Endlosschleife geraten. Vielen Dank für das neue Anforderungsbeispiel. Java-Methoden zum Umgang mit Dingen sind für mich sehr uninteressant.
Erik Reppen
4
HttpClient kann nicht mit compileSdk 23 und höher verwendet werden
Tamás Kozmér
30

Vielleicht ist meine Antwort ziemlich spät, aber sie deckt die API unter und über 21 ab.

Um Header hinzuzufügen, sollten wir jede Anfrage abfangen und eine neue mit den erforderlichen Headern erstellen .

Daher müssen wir die in beiden Fällen aufgerufene Methode shouldInterceptRequest überschreiben : 1. für API bis Stufe 21; 2. für API Level 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;
            }

        }
   });

Wenn der Antworttyp verarbeitet werden soll, können Sie ihn ändern

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

zu

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

und Methode hinzufügen

        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;
        }
Sergey Bondarenko
quelle
1
Es tut mir leid, auf diesen alten Beitrag zu antworten, aber mit diesem Code versucht meine App, die Datei herunterzuladen (und es schlägt fehl), anstatt die Seite zu laden.
Giacomo M
Vielen Dank!
AlexS
21

Wie bereits erwähnt, können Sie dies tun:

 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);

Ich habe dies und das mit einem MVC-Controller getestet und das Berechtigungsattribut erweitert, um den Header zu überprüfen, und der Header ist vorhanden.

leeroya
quelle
Ich muss das noch einmal ansprechen, als es geschrieben und gepostet wurde und mit dem Kit-Kat funktionierte. Ich habe es nicht mit Lolly Pop versucht.
Leeroya
Ich arbeite nicht für Jelly Bean oder Marshmallow ... ändert nichts an den Headern
Erik Verboom
6
Dies macht nicht das, was OP verlangt. Er möchte allen Anforderungen der Webansicht Header hinzufügen. Dies fügt benutzerdefinierte Header nur zur ersten Anfrage
NinjaCoder
Dies ist nicht, was OP fragt
Akshay
Ich weiß, dass dies nicht das beantwortet, wonach OP gesucht hat, aber genau das wollte ich, dh das Hinzufügen eines zusätzlichen Headers zu einer WebViewIntent-URL. Trotzdem danke!
Joshua Pinter
9

Das funktioniert bei mir:

  1. Zuerst müssen Sie eine Methode erstellen, die Ihre Header zurückgibt, die Sie zur Anforderung hinzufügen möchten:

    private Map<String, String> getCustomHeaders()
    {
        Map<String, String> headers = new HashMap<>();
        headers.put("YOURHEADER", "VALUE");
        return headers;
    }
  2. Zweitens müssen Sie WebViewClient erstellen:

    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. Fügen Sie WebViewClient zu Ihrem WebView hinzu:

    webView.setWebViewClient(getWebViewClient());

Hoffe das hilft.

eltray
quelle
1
Sieht gut aus , aber nicht diese fügen einen Header, oder hat diese ersetzen die Header?
Ivo Renkema
@IvoRenkema loadUrl(String url, Map<String, String> additionalHttpHeaders) bedeutet, zusätzliche Header hinzuzufügen
AbhinayMe
4

Sie sollten in der Lage sein, alle Ihre Header zu steuern, indem Sie loadUrl überspringen und Ihre eigene loadPage mit Javas HttpURLConnection schreiben. Verwenden Sie dann die loadData der Webansicht, um die Antwort anzuzeigen.

Es gibt keinen Zugriff auf die von Google bereitgestellten Header. Sie befinden sich in einem JNI-Aufruf tief in der WebView-Quelle.

R Earle Harris
quelle
1
Haben Sie einen Verweis auf das, was Sie in Ihrer Antwort sagen? Es ist für andere hilfreich, wenn Sie mit Ihren Antworten Implementierungsreferenzen angeben.
Hirdesh Vishwdewa
1

Hier ist eine Implementierung mit 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
    }
}

Beachten Sie, dass dies bei POST-Anforderungen nicht funktioniert, da WebResourceRequest keine POST-Daten bereitstellt. Es gibt eine Request Data - WebViewClient-Bibliothek, die eine JavaScript-Injection-Problemumgehung zum Abfangen von POST-Daten verwendet.

Miloš Černilovský
quelle
0

Das hat bei mir funktioniert. Erstellen Sie den folgenden WebViewClient und stellen Sie den Webclient auf Ihre Webansicht ein. Ich musste webview.loadDataWithBaseURL verwenden, da meine URLs (in meinem Inhalt) nicht die Basis-URL hatten, sondern nur relative URLs. Sie erhalten die URL nur dann korrekt, wenn mit loadDataWithBaseURL eine Basis-URL festgelegt wurde.

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;
            }
        }
    };

}
Shekar
quelle
für mich funktioniert es: .post (reqbody) wobei RequestBody reqbody = RequestBody.create (null, "");
Karoly
-1

Sie können dies verwenden:

@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;

            }
demir
quelle
1
Dadurch wird die URL immer wieder neu geladen, nicht wahr?
Onheiron
-3

Ich bin auf das gleiche Problem gestoßen und habe es gelöst.

Wie bereits erwähnt, müssen Sie Ihren benutzerdefinierten WebViewClient erstellen und die Methode shouldInterceptRequest überschreiben.

WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)

Diese Methode sollte eine webView.loadUrl ausgeben, während eine "leere" WebResourceResponse zurückgegeben wird.

Etwas wie das:

@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);
}
Francesco
quelle
Das Aufrufen von view.loadUrl von dieser Methode scheint die App zum Absturz zu bringen
willcwf
@willcwf hast du ein Beispiel für diesen Absturz?
Francesco
@Francesco meine App stürzt auch ab
Giacomo M
Zu alles stimmt ab und sagt, dass ein Absturz nicht hilft. Bitte seien Sie genauer, schreiben Sie einige Fehlerinformationen.
Francesco
-14

Benutze das:

webView.getSettings().setUserAgentString("User-Agent");
Satish
quelle
11
Dies beantwortet die Frage nicht
younes0
Dies ist nicht dasselbe wie der Autorisierungsheader
Vlad