解决android native包webview,webview中的请求blocked by CORS policy
Ranye123 2024-08-10 10:03:01 阅读 56
在stack overflow查,差不多查到的都是些webView.getSettings().setxxx,没用。在github上找别的类似的android native包webview运行pwa的项目,把它们的webView.getSettings().setxxx全搬过来,写了有一页多,一个有用的都没有,他们开发没有遇到过同源限制嘛!!!
还有一种,配置安卓项目本身的network,就是写一个network_security_config.xml,把服务器地址放到subdomain里。这个听了不大靠谱,因为同源策略是浏览器的限制,换言之,是webview的限制,应该要配置webview相关的,而不是app相关
查到的另外一种较多的方法是override shouldInterceptRequest,意思就是拦截request,然后进行一些这样那样的操作,帮助服务端返回数据,这个是最靠谱的。把他们的代码统统搬过来一个一个试了,都用不了,看来得自己写了
其实关于shouldInterceptRequest,官方文档是有example的,不过看起来虽然写了什么with same-origin policy,它主要的意思是同“协议”的源。同源策略不是说协议,域名,端口一致嘛,这些本地文件本来是file://,协议不一样,获取不到的,它通过assetLoader转一下,把file://xxx.js转成https://appassets.androidplatform.net/xxx.js,这样协议就都是https了,就能获取到并加载到webview中了:
https://developer.android.com/develop/ui/views/layout/webapps/load-local-content#assetloader
让server那边加Access-Control-Allow-Origin响应头,不现实,我们只是做poc,让人家好好的服务端改数据,怎么可能,人家能理咱?
之前我用react native开发过一版,也是跑在android tv上的,用的同一套restapi,为啥当时非常没遇到这些问题啊
搜了一下官方文档,好家伙,XMLHttpRequest是不存在CORS问题的:
https://reactnative.dev/docs/network#using-other-networking-libraries
好嘛,那要是这样说的话,直接拦截url,然后用XMLHttpRequest去请求,然后把响应返回回去,不就好了(智将
结果在那边满头大汗弄了半天,好不容易XMLHttpRequest转过去了,但怎么也没把XMLHttpResponse转成shouldInterceptRequest需要返回的WebResourceResponse,本来就不会kotlin,全组就我一个人弄这个,硬着头皮嗯写,写的烦死了
突然灵机一动,既然转的太麻烦,干脆不转了,同源策略限制是吧,我自己安一个响应头上去!
<code> private class LocalContentWebViewClient(private val assetLoader: WebViewAssetLoader) :
WebViewClientCompat() {
val INTERCEPT_KEY_LOCAL_ASSET = "https://appassets.androidplatform.net/"
val client = OkHttpClient()
var headers: MutableMap<String, String> = java.util.HashMap()
init {
headers["Access-Control-Allow-Origin"] = "*"
headers["Access-Control-Allow-Headers"] =
"隐去,按照您的实际请求的headers来写"
}
fun requestDataFromServer(request: WebResourceRequest): ResponseBody? {
val url = request.url.toString()
var responseBody: ResponseBody? = null;
try {
val req = Request.Builder()
.url(url)
.headers(request.requestHeaders.toHeaders())
.build()
val call = client.newCall(req)
val resp = call.execute()
responseBody = resp.body
} catch (e: Exception) {
println("url failed" + url + e.toString());
}
return responseBody
}
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
val url = request.url.toString()
return if (url.contains(INTERCEPT_KEY_LOCAL_ASSET)) {
assetLoader.shouldInterceptRequest(request.url)
} else if (url.contains(".jpeg")) {
val responseBody = requestDataFromServer(request)
val data: InputStream? = responseBody?.byteStream()
return WebResourceResponse(
"image/jpeg",
"UTF-8",
200,
"OK",
headers,
data
)
} else if (url.contains(".png")) {
val responseBody = requestDataFromServer(request)
val data: InputStream? = responseBody?.byteStream()
return WebResourceResponse(
"image/png",
"UTF-8",
200,
"OK",
headers,
data
)
} else {
val responseBody = requestDataFromServer(request)
val data: InputStream? = responseBody?.byteStream()
return WebResourceResponse(
"application/json",
"UTF-8",
200,
"OK",
headers,
data
)
}
}
}
代码就贴在这里了,供各位同样受困于这个问题的朋友们参考,第一个url.contains(INTERCEPT_KEY_LOCAL_ASSET),就是本地的html css js文件,按照android developer官方写法就好;其他的三种是一个意思,在外面用okhttp库获取response,安上跨域的header,把response body塞到里面stream里面返回,唯一的不同就是MIME type的不同,图片格式不能用"application/json",要分开
当然如果优化的还有很大的优化空间,比方说okhttp请求用异步的写法更好,还有直接把status code设置为200,肯定是不对的,我只是一个做poc的,懒得改了,验证完可行性给领导展示了就完了,相信server端同事们的水平(x) 大家如果参考记得改改嗷
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。