[セットトップ]react nativeはfetchを使ってネットワーク要求(https)を行い、SSLHandschake問題を解決し、どのように二次パッキングを行いますか?


react nativeはfetchを使ってネットワーク要求を行う(https問題SSLHandschake、解決方法)
  • 使用例
  • カプセル化はどのように行いますか?
  • よくある問題(タイムアウト設定、無効なssl証明書、…)
  • fetchの原理解説
  • インスタンスを使う
    1.get方式を用いたネットワーク要求、例えば:
    fetch('http://nero-zou.com/test', {  
        method: 'GET'
    }).then(function(response) {
        //    ,    
    }).catch(function(err) {
        //    
    });
    2.post方式を使用してネットワーク要求、例えば:
    let param = {user:'xxx',phone:'xxxxxx'};
    fetch(url, {  
        method: 'post',
        body: JSON.stringify(param)
    }).then(function(response) {
        //    ,    
    });
    3.他の書き方、例えば:
    try {
           fetch(url, {  
                method: 'post',
                body: JSON.stringify(param)
            }).then(function(response) {
                //    ,    
            });  
        } catch(e) {
             //       
        }
    4.headerまたは他のパラメータを持つ
    fetch(url, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', },
      body: JSON.stringify({ firstParam: 'yourValue', secondParam: 'yourOtherValue', })
    })
    
    パッケージはどうしますか
    基本的に要求されるのは基本的な関数です.他のネットワーク要求時にはこの関数を呼び出します.今後はfetchを使わずにこの基礎関数を直接修正しても、他のところを修正しなくても実現できます.
    基本関数のモデルは一般的にこのようです.
    function sendNetRequest(...props) {
      this.url = props.shift(1);
      this.options = props.shift(1);   
      return fetch(this.url, Object.assign({}, this.options))
      .then((response) =>return response.json());
    }
    各インターフェースを実装
     //  login   
     function  postLogin(userName,password) {  
        let loginParam= {user:userName,password:password};    
        var loginApiPort = "mlogin";//login      
        //   baseURL=https://XXXXX.com 
        return sendNetRequest(`${baseURL}/${loginApiPort}`, {
          method: 'post',
          body: JSON.stringify(loginParam),
          headers: {
               'Content-Type': 'application/x-www-form-urlencoded',
           },
        });
      }
      //...          
    呼び出しのインスタンス
      try {
             postLogin(user,password)
             .then((response) => { //    ,     }) } catch(e) { //       }
    これで成功しました.次はよくある問題です.
    よくある問題
    1.要求時に異常が発生する
    headerの中のContentt-Typeを'appication/x-wn-form-urlencoded'に設定します.もしまだserver端パラメータがどんなフォーマットなのかを聞き間違えたら、Content-Typeの値を設定すればいいです.
    2.応答時に異常が発生する
    上記のパッケージは簡単に問題が発生します.この文の中で、レスポンス==nullが異常を出すと、まずレスポンスがnullかどうかを判断し、nullの場合は特殊処理を行うことをお勧めします.
    3.fetch設定のタイムアウト時間
    fetch自体は現在タイムアウト時間の属性が設定されていませんので、設定するしかありません.fetch関数は各プラットフォームで実装されています.ソースコードを見たら、タイムアウトを設定するのは簡単だと思いますが、カプセル化時にはこれを一つの属性として設定していません.したがって、promiseメカニズムに合わせてsetTimeoutを使用してタイムアウトの時間を設定するしかないです.
    4.https、server端が無効証明書でhttps要求した時に発生したエラー
    SSLHandschake:Received fatal alert:certificate_expiredまたはSSLHandschake:Remote host closed connection during hadshak…
    (1)Androidでは、httpしか使えません.ライブラリの中の関数は変えられません.httpsをサポートしなければならないなら、あなたのプロジェクトのカタログを+node_modules/react-native/android/com/facebook/react-native/0.6.0/react-native-06.0-sources.jar!/com/facebook/react/modules/network/OkHttp ClientProvider.java、
    他のrnバージョンのファイルディレクトリは、総じてreactnativeのnetworkライブラリ内のOkHttpClientProvider.javaというファイルを修正したと推測できます.
    OkHttp ClientProvider.javaの中で下記のコードを見つけました.
     return new OkHttpClient.Builder()
          .connectTimeout(0, TimeUnit.MILLISECONDS)
          .readTimeout(0, TimeUnit.MILLISECONDS)
          .writeTimeout(0, TimeUnit.MILLISECONDS)
          .cookieJar(new ReactCookieJarContainer())
          .build();
    変更:
     return new OkHttpClient.Builder()
                .sslSocketFactory(sslContext.getSocketFactory())
                .hostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true; //       ,     true
                    }
                })
                .connectTimeout(0, TimeUnit.MILLISECONDS)
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .writeTimeout(0, TimeUnit.MILLISECONDS)
                .cookieJar(new ReactCookieJarContainer())
                .build();
    
    2.iOS端、xcodeでiosディレクトリ下のプロジェクトを開け、infor.plistを見つけ、属性を追加する.
      <key>NSAppTransportSecurity</key>
        <dict>
              <key>NSAllowsArbitraryLoads</key>
              <true/>
        </dict>
    やはりエラーです.React nativeのnetworkingライブラリを見つけて、倉庫にNSURLSession delegate関数を追加してssl証明書認証の関連コードを処理して、全部パスに設定します.debugモードかどうかによってパスかどうかを選択してもいいです.release版であれば、やめてください.この関数のように
    //              ,         pass,     ,   pass     http    。
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
    5.忘れました.画像をアップロードする時に発生した問題のようです.後で追加します.
    原理解説、各プラットフォームでfetch実現のカギコード
    react nativeのネットワーク操作はよく使われるfetchであり、fetch関数の実現はiOSとandroidプラットフォームでそれぞれRCTNetworkingを使用して実現されることがわかった.上記の4つ目の問題の解決は、RCTNetworkingソースコードを見ることによって解決されます.
    android上のキーコード分析:
    以下は実現されるRCT Networkingです.よく見るとOkHttpClientで実現されます.
    @ReactModule(name = "RCTNetworking", supportsWebWorkers = true)
    public final class NetworkingModule extends ReactContextBaseJavaModule {
    
      private static final String CONTENT_ENCODING_HEADER_NAME = "content-encoding";
      private static final String CONTENT_TYPE_HEADER_NAME = "content-type";
      private static final String REQUEST_BODY_KEY_STRING = "string";
      private static final String REQUEST_BODY_KEY_URI = "uri";
      private static final String REQUEST_BODY_KEY_FORMDATA = "formData";
      private static final String USER_AGENT_HEADER_NAME = "user-agent";
      private static final int CHUNK_TIMEOUT_NS = 100 * 1000000; // 100ms
      private static final int MAX_CHUNK_SIZE_BETWEEN_FLUSHES = 8 * 1024; // 8K
    
      private final OkHttpClient mClient;//        OkHttpClient
      private final ForwardingCookieHandler mCookieHandler;
      private final @Nullable String mDefaultUserAgent;
      private final CookieJarContainer mCookieJarContainer;
      private final Set<Integer> mRequestIds;
      private boolean mShuttingDown;
    
      /* package */ NetworkingModule(
          ReactApplicationContext reactContext,
          @Nullable String defaultUserAgent,
          OkHttpClient client,
          @Nullable List<NetworkInterceptorCreator> networkInterceptorCreators) {
        super(reactContext);
    
        if (networkInterceptorCreators != null) {
          OkHttpClient.Builder clientBuilder = client.newBuilder();
          for (NetworkInterceptorCreator networkInterceptorCreator : networkInterceptorCreators) {
            clientBuilder.addNetworkInterceptor(networkInterceptorCreator.create());
          }
          client = clientBuilder.build();
        }
        mClient = client;
        OkHttpClientProvider.replaceOkHttpClient(client);
        mCookieHandler = new ForwardingCookieHandler(reactContext);
        mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
        mShuttingDown = false;
        mDefaultUserAgent = defaultUserAgent;
        mRequestIds = new HashSet<>();
      } 
    OkHttp ClidentProviderは、ネットワーク要求を管理するために、単一のインスタンスクラスを作成します.
    public class OkHttpClientProvider {
    
      // Centralized OkHttpClient for all networking requests.
      private static @Nullable OkHttpClient sClient;
    
      public static OkHttpClient getOkHttpClient() {
        if (sClient == null) {
          //sClient = createClient();
          sClient=createClient_initNetworkConfig();
        }
        return sClient;
      }
    
      // okhttp3 OkHttpClient is immutable
      // This allows app to init an OkHttpClient with custom settings.
      public static void replaceOkHttpClient(OkHttpClient client) {
        sClient = client;
      }
    
      private static OkHttpClient createClient() {
        // No timeouts by default
        return new OkHttpClient.Builder()
          .connectTimeout(0, TimeUnit.MILLISECONDS)
          .readTimeout(0, TimeUnit.MILLISECONDS)
          .writeTimeout(0, TimeUnit.MILLISECONDS)
          .cookieJar(new ReactCookieJarContainer())
          .build();
      }
    上記のコードによりタイムアウトは設定できますが、fetchはこの属性を実装していません.
    ios端キーコード説明
    RCT NetInfoクラスで実現される機能:ネットワークがWiFiかどうかを問い合わせる.RCT Networking類で実現される機能:AFNetworkingの実現と同じです.よく検討してください.とりあえず
    最後に完全なインスタンスを提供します.
    1.新規プロジェクト:
    実行コマンドreact-native init ZXJNetDemo
    2.コードの作成
    新規ファイルBaserviceApple Net.js:
    
    const baseURL = "https://api.app.net";
    function fetchAction(...props) {
      this.url = props.shift(1);
      this.options = props.shift(1);
      return fetch(this.url, Object.assign({}, this.options))
      .then((response) =>response.json());
    }
    export default {
      getTest() {
        var apiPort = "stream/0/posts/stream/global";
        return fetchAction(`${baseURL}/${apiPort}`, {
          method: 'get',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
        });
      }
    };
    ファイルindex.ios.js
    import React, { Component } from 'react';
    import {
      AppRegistry,
      StyleSheet,
      Text,
      View,
      ActivityIndicator
    } from 'react-native';
    import BaseServiceApiNet from './BaseServiceApiNet';
    export default class ZXJNetDemo extends Component {
      constructor(props){
        super(props);
        this.state ={
          isLoading:false,
          resultJson:null
        };
      }
      sendTestRequest(){
        if(this.state.isLoading==true){
          return;
        }
        this.setState({
          resultJson:null,
          isLoading:true
        });
        try {
            BaseServiceApiNet.getTest()
            .then((response) => { let data = response.meta; this.setState({ resultJson:data==null?null:JSON.stringify(data), isLoading:false }); console.log("    :"+JSON.stringify(data)); }) } catch(e) { alert(e); this.setState({ isLoading:false }); } } render() { return ( <View style={styles.container}> <ActivityIndicator animating={this.state.isLoading} /> <Text style={styles.welcome} onPress={this.sendTestRequest.bind(this)}>      </Text> <Text style={styles.instructions}> {this.state.resultJson} </Text> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, }); AppRegistry.registerComponent('ZXJNetDemo', () => ZXJNetDemo);
    3.運転結果: