WebView與JS、Ajax跨域問題

desaco發表於2016-02-23

》1.webview其實就是一個瀏覽器,而是否允許跨域,是由你訪問的伺服器控制的(預設不允許),如果是nginx,配置如下:
http {
  ......
  add_header Access-Control-Allow-Origin *;
  add_header Access-Control-Allow-Headers X-Requested-With;
  add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
  ......
}
其實就是在Http響應頭中加了點東西,其他的伺服器也類似這樣。
跨域的問題是javascript限制的。和webview或UIWebview都沒有關係。
當然跨域的解決方案有很多,可以自行google或百度之。
》2.Finally I've found the answer after 3 Long days. 
the problem was in the page that I request which have this code:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
in the response which conflict with android webview and doesn't work with jquery selectors ... Don't know why!!!
but when I removed the above code from response the page and its ajax works fine.
》3。
if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) 
  wv.getSettings().setAllowUniversalAccessFromFileURLs(true);
But if you want to compile against and support systems below API11 you have to add the following:


if (Build.VERSION.SDK_INT >= 16) {  
    Class<?> clazz = webView.getSettings().getClass();
    Method method = clazz.getMethod("setAllowUniversalAccessFromFileURLs", boolean.class);
    if (method != null) {
        method.invoke(webView.getSettings(), true);
    }
}
Different subdomains are not also allowed. Chrome was acting really wierd ( it was working ok for my "get" request , but was changing my "post" request to "OPTIONS" request ), it took me a lot time to figure out the error. The error was gone when i put the services and client code under the same subdomain.
Web services requires POST so i couldn't use JSONP and GET. Thanks for your helps, i think the best way, setting Access-Control-Allow-Origin: *
》4. 有多少次做webapp的時候,寫js會碰到 XMLHttpRequest cannot load http://abc Origin http://def  is not allowed by Access-Control-Allow-Origin. 跨域問題一直搞得很煩,不知道W3C怎麼想的,安全上又不能完全避免,寫程式又極不方便。
之前做瀏覽器時可以修改瀏覽器而實現,而在android上查了居然沒api。查閱程式碼樹發現webviewcore有相關介面,又被java的private修飾符給擋住(高階語言就是喜歡整一套規範,搞得n多人成天討論規範,而忘了程式設計的思想)。
吐槽完畢,思路及程式碼如下
思路:
訪問android WebView private 物件 WebViewCore mWebViewCore,呼叫 mWebViewCore的private 方法nativeRegisterURLSchemeAsLocal,把http和https忽悠成本地訪問。 XMLHttpRequest即可自由跨域。
注:android 4.1煎餅以上還需要設定WebSettings.setAllowUniversalAccessFromFileURLs(true) 

程式碼從WebView繼承一個新類,增加一個開啟跨域函式:
public class WokaoWebView extends WebView{
構造和其他函式省略.....
public void enablecrossdomain()
{
try{
Field field = WebView.class.getDeclaredField("mWebViewCore");
field.setAccessible(true);
Object webviewcore=field.get(this);
Method method=webviewcore.getClass().getDeclaredMethod("nativeRegisterURLSchemeAsLocal", String.class);
method.setAccessible(true);  
method.invoke(webviewcore, "http");
method.invoke(webviewcore, "https");
}
catch(Exception   e){
Log.d("wokao","enablecrossdomain error");
e.printStackTrace();
}
}
}

~~~~~~~~~
使用時,
WokaoWebView _web_app= (WokaoWebView)webapp_desktop.findViewById(R.id.webapp_page);
_web_app.enablecrossdomain();
_web_app.loadUrl("xxxx");
~~~~~~~~~~~~~~~~~~~~~~~~~~
android 4.1webview改動比較大。呼叫引數也多了一個,程式碼如下
public void enablecrossdomain41()
{
try{
Field webviewclassic_field = WebView.class.getDeclaredField("mProvider");
webviewclassic_field.setAccessible(true);
Object webviewclassic=webviewclassic_field.get(this);
Field webviewcore_field=webviewclassic.getClass().getDeclaredField("mWebViewCore");
webviewcore_field.setAccessible(true);
Object mWebViewCore=webviewcore_field.get(webviewclassic);
Field nativeclass_field=webviewclassic.getClass().getDeclaredField("mNativeClass");
nativeclass_field.setAccessible(true);
Object mNativeClass=nativeclass_field.get(webviewclassic);

Method method=mWebViewCore.getClass().getDeclaredMethod("nativeRegisterURLSchemeAsLocal",new Class[] {int.class,String.class});
method.setAccessible(true);
method.invoke(mWebViewCore,mNativeClass, "http");
method.invoke(mWebViewCore,mNativeClass, "https");
}
catch(Exception   e){
Log.d("wokao","enablecrossdomain error");
e.printStackTrace();
}
}

總結:最後一點解決了問題。。。

相關文章