向WebView资源请求添加自定义标头 - android

Ray*_*Ray 85 java android webkit android-webview

我需要为来自WebView的每个请求添加自定义标头.我知道loadURL有参数extraHeaders,但这些只适用于初始请求.所有后续请求都不包含标头.我查看了所有覆盖内容WebViewClient,但没有任何内容允许向资源请求添加标头 - onLoadResource(WebView view, String url).任何帮助都会很精彩.

谢谢,雷

pec*_*eps 72

尝试

loadUrl(String url, Map<String, String> extraHeaders)
Run Code Online (Sandbox Code Playgroud)

要将标头添加到资源加载请求,请创建自定义WebViewClient并覆盖:

API 24+:
WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)
or
WebResourceResponse shouldInterceptRequest(WebView view, String url)
Run Code Online (Sandbox Code Playgroud)

  • 是的,覆盖WebClient.shouldOverrideUrlLoading,如下所示:public boolean shouldOverrideUrlLoading(WebView view,String url){view.loadUrl(url,extraHeaders); 返回true; } (17认同)
  • 对不起,但这不起作用.它仅对初始请求应用标头.标头不会添加到资源请求中.其他想法?谢谢. (7认同)
  • @peceps - 在资源加载期间不调用回调'shouldOverrideUrlLoading'.例如,当我们尝试`view.loadUrl("http://www.facebook.com",extraHeaders)`时,有多个资源请求,如`'http://static.fb.com/images/logo.png从webiew发送的"等等.对于这些请求,不会添加额外的标头.并且在此类资源请求期间不会调用shouldOverrideUrlLoading.调用回调'OnLoadResource',但此时无法设置标题. (5认同)
  • @yorkw:此方法捕获所有资源请求URL.但是没有办法为这些请求添加标头.我的目标是为所有请求添加自定义HTTP标头.如果可以使用`shouldInterceptRequest`方法实现这一点,您能解释一下吗? (3认同)
  • @MediumOne,用于加载资源,请覆盖`WebViewClient.shouldInterceptRequest(android.webkit.WebView视图,java.lang.String url)`签出[API](http://developer.android.com/reference/android/webkit/ WebViewClient.html)。 (2认同)

Mar*_*cny 33

您需要使用WebViewClient.shouldInterceptRequest拦截每个请求

每次拦截时,您需要获取网址,自己发出此请求,然后返回内容流:

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);
Run Code Online (Sandbox Code Playgroud)

如果您的最低API目标是21级,则可以使用新的shouldInterceptRequest,它会为您提供其他请求信息(例如标题),而不仅仅是URL.

  • HttpClient不能与compileSdk 23及更高版本一起使用, (4认同)
  • @HirdeshVishwdewa - 看最后一句话. (3认同)
  • 以防有人在使用这个技巧时遇到同样的情况.(无论如何,这是一个很好的.)这是给你的一张便条.由于http content-type标头(可以包含charset等可选参数)与MIME类型不完全兼容,因此需要WebResourceResponse构造函数的第一个参数,因此我们应该从内容类型中提取MIME类型部分.可以想到,例如RegExp,使其适用于大多数情况. (2认同)
  • 此事件已弃用.. 使用 `public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)` 代替找到更多 [here](http://bit.ly/1KXFgAb) (2认同)
  • 您可以通过将webc和修改后的请求作为参数返回超类的shouldInterceptRequest方法的结果来跳过自己的负载.这在您基于URL触发的情况下特别方便,在重新加载时不会更改它并且会遇到无限循环.非常感谢新的请求示例.Java处理事物的方式对我来说非常违反直觉. (2认同)
  • 不幸的是,在向请求对象添加标头后调用“super.shouldInterceptRequest()”并不会像@ErikReppen 所描述的那样发出修改后的请求。如果可以的话那就太好了,但这不是 API 的工作方式。super 调用只是返回 null 而不发出请求。返回 null 告诉 Web 客户端不要拦截请求,并且请求是在没有修改标头的情况下发出的。 (2认同)

Ser*_*nko 25

也许我的反应很晚,但它涵盖了API 以下和21级以上.

要添加标头,我们应该拦截每个请求并使用所需的标头创建新的请求.

因此我们需要覆盖在两种情况下调用的shouldInterceptRequest方法:1.用于API直到21级; 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;
            }

        }
   });
Run Code Online (Sandbox Code Playgroud)

如果应该处理响应类型,您可以更改

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

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

并添加方法

        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;
        }
Run Code Online (Sandbox Code Playgroud)

  • 很抱歉回答这个旧帖子,但使用此代码我的应用程序尝试下载文件(但失败)而不是加载页面。 (2认同)

lee*_*oya 20

如前所述,您可以这样做:

 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);
Run Code Online (Sandbox Code Playgroud)

我测试了这个,并使用MVC控制器,我扩展了授权属性以检查标头,标头就在那里.

  • 这不符合OP的要求。他想将标题添加到Webview发出的所有请求中。这会将自定义标头添加到仅第一个请求 (3认同)

elt*_*ray 7

这对我有用:

  1. 首先,您需要创建方法,该方法将返回您要添加到请求的标头:

    private Map<String, String> getCustomHeaders()
    {
        Map<String, String> headers = new HashMap<>();
        headers.put("YOURHEADER", "VALUE");
        return headers;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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;
        }
    };
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 将WebViewClient添加到WebView:

    webView.setWebViewClient(getWebViewClient());
    
    Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.


Mil*_*ský 5

这是使用 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
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这不适用于 POST 请求,因为 WebResourceRequest 不提供 POST 数据。有一个请求数据 - WebViewClient 库,它使用 JavaScript 注入解决方法来拦截 POST 数据。