web-dev-qa-db-fra.com

Ajouter des en-têtes personnalisés aux demandes de ressources WebView - Android

J'ai besoin d'ajouter des en-têtes personnalisés à CHAQUE demande provenant de WebView. Je sais que loadURL a le paramètre pour extraHeaders, mais ceux-ci ne sont appliqués qu'à la demande initiale. Toutes les demandes ultérieures ne contiennent pas les en-têtes. J'ai examiné toutes les substitutions dans WebViewClient, mais rien ne permet d'ajouter des en-têtes aux demandes de ressources - onLoadResource(WebView view, String url). Toute aide serait merveilleuse.

Merci, Ray 

72
Ray

Essayer 

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

Pour ajouter des en-têtes aux demandes de chargement des ressources, personnalisez WebViewClient et remplacez:

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

Vous devrez intercepter chaque requête à l’aide de WebViewClient.shouldInterceptRequest

A chaque interception, vous devrez prendre l'URL, effectuer cette demande vous-même et renvoyer le flux de contenu:

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

Si votre cible d'API minimale est de niveau 21, vous pouvez utiliser le nouveau shouldInterceptRequest qui vous donne des informations de requête supplémentaires (telles que des en-têtes) au lieu de l'URL uniquement.

29
Martin Konecny

Comme mentionné précédemment, vous pouvez faire ceci:

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

J'ai testé cela et sur avec un contrôleur MVC que j'ai étendu l'attribut d'autorisation pour inspecter l'en-tête et l'en-tête est là. 

17
leeroya

Ma réponse est peut-être un peu tardive, mais elle couvre le niveau API en bas et en haut 21.

Pour ajouter des en-têtes, nous devrions intercepter chaque requête et en créer une nouvelle avec les en-têtes requis.

Nous devons donc redéfinir la méthode shouldInterceptRequest appelée dans les deux cas: 1. pour API jusqu'au niveau 21; 2. pour API niveau 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;
            }

        }
   });

Si le type de réponse doit être traité, vous pouvez changer

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

et ajouter une méthode

        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;
        }
17
Sergey Bondarenko

Cela fonctionne pour moi:

  1. Vous devez d’abord créer la méthode, qui retournera les en-têtes .__ que vous souhaitez ajouter à la requête:

    private Map<String, String> getCustomHeaders()
    {
        Map<String, String> headers = new HashMap<>();
        headers.put("YOURHEADER", "VALUE");
        return headers;
    }
    
  2. Deuxièmement, vous devez créer 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. Ajoutez WebViewClient à votre WebView:

    webView.setWebViewClient(getWebViewClient());
    

J'espère que cela t'aides.

6
eltray

Vous devriez être capable de contrôler tous vos en-têtes en sautant loadUrl et en écrivant votre propre loadPage à l'aide de HttpURLConnection de Java. Ensuite, utilisez le loadData de la vue Web pour afficher la réponse.

Il n'y a pas d'accès aux en-têtes fournis par Google. Ils sont dans un appel JNI, profondément dans la source WebView.

4
R Earle Harris

Cela a fonctionné pour moi. Créez WebViewClient comme ci-dessous et définissez le client Web sur votre vue Web. Je devais utiliser webview.loadDataWithBaseURL car mes URL (dans mon contenu) n'avaient pas le baseurl mais seulement des URL relatives. Vous obtiendrez correctement l’URL uniquement s’il existe un ensemble baseurl utilisant loadDataWithBaseURL.

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

}
1
Shekar

Je suis tombé sur le même problème et résolu.

Comme indiqué précédemment, vous devez créer votre WebViewClient personnalisé et substituer la méthode shouldInterceptRequest. 

WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)

Cette méthode doit générer un fichier webView.loadUrl en renvoyant un "vide" WebResourceResponse.

Quelque chose comme ça:

@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);
}
0
Francesco

Voici une implémentation utilisant 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
    }
}

Notez que cela ne fonctionne pas pour les requêtes POST car WebResourceRequest ne fournit pas de données POST. Il existe une bibliothèque Request Data - WebViewClient qui utilise une solution de contournement d'injection JavaScript pour intercepter les données POST.

0