React 本机自定义 webview 在开发时防止 SSL 错误
React native custom webview prevent SSL errors while developing
问题
尝试在 Android.
上使用 react-native-webview
连接到我的本地开发服务器
您可以使用 http://10.0.0.2
作为默认网关来连接到模拟器上的本地计算机。或者,我 运行 在我的 phone 上构建并连接到我机器的 IPV4 地址。
我需要 Crypto API 并且只能在 HTTPS 上使用,这意味着我需要连接到 https://10.0.0.2
.
我没有使用自签名证书。
快速而肮脏的解决方案
在com.reactnativecommunity.webview
包的onReceivedSslError
方法的第一行调用handler.proceed()
。
虽然这可能有效,但它不是首选方式,因为它不受源代码控制,每次制作发布版本或全新安装项目时都需要删除。
解决方案
查看下面的答案
在开发版本上创建自定义 webview 以规避 SSL 错误(或任何其他 webview 方法)。
我的解决方案
1. 仅创建调试 class CustomWebviewManager.java
我们不希望我们的自定义 webview 管理器在发布版本中结束。
package nl.myapp;
import android.webkit.WebView;
import android.webkit.SslErrorHandler;
import android.net.http.SslError;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.reactnativecommunity.webview.RNCWebViewManager;
@ReactModule(name = CustomWebViewManager.REACT_CLASS)
public class CustomWebViewManager extends RNCWebViewManager {
protected static final String REACT_CLASS = "RNCCustomWebView"; // React native import name
protected static class CustomWebviewClient extends RNCWebViewClient {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error){
// Prevent SSL errors
handler.proceed();
}
}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
// Set our custom client as webview client
view.setWebViewClient(new CustomWebviewClient());
}
}
2.新建class注册React PackagesMyappAppPackage.java
我们应该只在调试模式下注册调试 class 并作为 ViewManager
.
下一步注册我们的包后React Native会自动调用createViewManagers
使用 BuildConfig.DEBUG
仅在调试版本中执行。
我们不能直接实例化我们的 class,因为它会导致产品构建错误。这就是为什么我们使用 Class.forName
package nl.myapp;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyappAppPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> viewManagers = new ArrayList<>();
if (BuildConfig.DEBUG) {
// Add custom webview manager to circumvent SSL errors
try {
Class<ViewManager> c = (Class<ViewManager>) Class.forName("nl.myapp.CustomWebViewManager");
viewManagers.add(c.newInstance());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
return viewManagers;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
3.注册我们的包
package nl.myapp;
import android.app.Application;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new MyappAppPackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
...
}
4. 最后在开发时使用我们自定义的webview
将它放在单独的文件中,否则热重载将不起作用,因为 requireNativeComponent
会尝试再次注册 ViewManager
src/components/native/RNCCustomWebView.js
import {requireNativeComponent} from 'react-native';
module.exports = requireNativeComponent('RNCCustomWebView');
src/components/webview.js
import React from 'react';
import { WebView } from 'react-native-webview';
import RNCCustomWebView from './native/RNCCustomWebView';
// ...
const renderWebView = () => {
// ...
const nativeConfig = {};
if (__DEV__) { // __DEV__ is set by RN on debug builds
// Set custom component to circumvent SSL errors
nativeConfig.component = RNCCustomWebView;
}
return (
<WebView
nativeConfig={nativeConfig}
source="https://10.0.0.1"
/>
);
};
问题
尝试在 Android.
上使用 react-native-webview
连接到我的本地开发服务器
您可以使用 http://10.0.0.2
作为默认网关来连接到模拟器上的本地计算机。或者,我 运行 在我的 phone 上构建并连接到我机器的 IPV4 地址。
我需要 Crypto API 并且只能在 HTTPS 上使用,这意味着我需要连接到 https://10.0.0.2
.
我没有使用自签名证书。
快速而肮脏的解决方案
在com.reactnativecommunity.webview
包的onReceivedSslError
方法的第一行调用handler.proceed()
。
虽然这可能有效,但它不是首选方式,因为它不受源代码控制,每次制作发布版本或全新安装项目时都需要删除。
解决方案
查看下面的答案
在开发版本上创建自定义 webview 以规避 SSL 错误(或任何其他 webview 方法)。
我的解决方案
1. 仅创建调试 class CustomWebviewManager.java
我们不希望我们的自定义 webview 管理器在发布版本中结束。
package nl.myapp;
import android.webkit.WebView;
import android.webkit.SslErrorHandler;
import android.net.http.SslError;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.reactnativecommunity.webview.RNCWebViewManager;
@ReactModule(name = CustomWebViewManager.REACT_CLASS)
public class CustomWebViewManager extends RNCWebViewManager {
protected static final String REACT_CLASS = "RNCCustomWebView"; // React native import name
protected static class CustomWebviewClient extends RNCWebViewClient {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error){
// Prevent SSL errors
handler.proceed();
}
}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
// Set our custom client as webview client
view.setWebViewClient(new CustomWebviewClient());
}
}
2.新建class注册React PackagesMyappAppPackage.java
我们应该只在调试模式下注册调试 class 并作为 ViewManager
.
下一步注册我们的包后React Native会自动调用createViewManagers
使用 BuildConfig.DEBUG
仅在调试版本中执行。
我们不能直接实例化我们的 class,因为它会导致产品构建错误。这就是为什么我们使用 Class.forName
package nl.myapp;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyappAppPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> viewManagers = new ArrayList<>();
if (BuildConfig.DEBUG) {
// Add custom webview manager to circumvent SSL errors
try {
Class<ViewManager> c = (Class<ViewManager>) Class.forName("nl.myapp.CustomWebViewManager");
viewManagers.add(c.newInstance());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
return viewManagers;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
3.注册我们的包
package nl.myapp;
import android.app.Application;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new MyappAppPackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
...
}
4. 最后在开发时使用我们自定义的webview
将它放在单独的文件中,否则热重载将不起作用,因为 requireNativeComponent
会尝试再次注册 ViewManager
src/components/native/RNCCustomWebView.js
import {requireNativeComponent} from 'react-native';
module.exports = requireNativeComponent('RNCCustomWebView');
src/components/webview.js
import React from 'react';
import { WebView } from 'react-native-webview';
import RNCCustomWebView from './native/RNCCustomWebView';
// ...
const renderWebView = () => {
// ...
const nativeConfig = {};
if (__DEV__) { // __DEV__ is set by RN on debug builds
// Set custom component to circumvent SSL errors
nativeConfig.component = RNCCustomWebView;
}
return (
<WebView
nativeConfig={nativeConfig}
source="https://10.0.0.1"
/>
);
};