将自定义头添加到 WebView 资源请求-android

我需要添加来自 WebView 的每个请求自定义头。我知道 loadURLextraHeaders的参数,但是这些参数只应用于初始请求。所有后续请求都不包含标头。我已经查看了 WebViewClient中的所有重写,但是没有一个允许向资源请求中添加头文件—— onLoadResource(WebView view, String url)。任何帮助都是好的。

谢谢, 雷

159327 次浏览

试试看

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

For adding headers to resources loading requests, make custom WebViewClient and override:

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

用这个:

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

您应该能够通过跳过 loadUrl 并使用 Java 的 HttpURLConnection 编写自己的 loadPage 来控制所有的标头。然后使用 webview 的 loadData 来显示响应。

无法访问谷歌提供的页眉。它们在一个 JNI 调用中,深入到 WebView 源代码中。

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

 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 属性来检查头部,头部就在那里。

您需要使用 应该拦截请求拦截每个请求

对于每次拦截,您都需要获取 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。

你可以用这个:

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


}

这招对我很管用。像下面这样创建 WebViewClient,并将 webclient 设置为 webview。我必须使用 webview.loadDataWithBaseURL,因为我的 url (在我的内容中)没有 baseurl,只有相对 url。只有在使用 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;
}
}
};


}

这对我有用:

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

    private Map<String, String> getCustomHeaders()
    {
    Map<String, String> headers = new HashMap<>();
    headers.put("YOURHEADER", "VALUE");
    return headers;
    }
    
  2. Second you need to create 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. Add WebViewClient to your WebView:

    webView.setWebViewClient(getWebViewClient());
    

Hope this helps.

也许我的回应相当晚,但它涵盖了 API 下面以上21级。

要添加标题,我们应该 拦截每一个请求创造一个新的与所需的标题。

因此,我们需要覆盖在这两种情况下调用的 应该拦截请求方法: 1. 空气污染指数直至第21级; 2. 空气污染指数级别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;
}

我遇到了同样的问题并解决了。

如前所述,您需要创建自定义 WebViewClient 并覆盖 should dInterceptRequest 方法。

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

下面是一个使用 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
}
}

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