WeChatミニプログラムにおけるネットワークリクエストとインターセプターの実装パターン

ミニプログラム開発において、API通信処理の一元化は保守性とセキュリティの両面で不可欠です。`wx.request`をプロミスベースのラッパーで包み、リクエスト・レスポンスの中間処理(インターセプター)を分離することで、認証トークンの自動付与や共通エラーハンドリングを画面ロジックから完全に切り離すことができます。

通信ディスパッチャーの構築

各HTTPメソッドごとに独立した関数を用意する従来の方式はコードが冗長になりやすいため、メソッド名を動的に設定する単一ゲートウェイ関数と、設定オブジェクトの組み合わせを採用します。

// httpClient.js
const coreDispatcher = (config) => {
  return new Promise((onSuccess, onFail) => {
    wx.request({
      url: config.targetUrl,
      method: config.httpMethod || 'GET',
      data: config.payload || {},
      header: config.customHeaders || {},
      success: onSuccess,
      fail: onFail
    });
  });
};

// 各メソッド用ラッパー(設定の事前マージ)
const apiGateway = {
  get: (opts) => coreDispatcher({ ...opts, httpMethod: 'GET' }),
  post: (opts) => coreDispatcher({ ...opts, httpMethod: 'POST' }),
  put: (opts) => coreDispatcher({ ...opts, httpMethod: 'PUT' }),
  delete: (opts) => coreDispatcher({ ...opts, httpMethod: 'DELETE' })
};

export { coreDispatcher, apiGateway };

インターセプターチェーンの実装

リクエスト送信前のヘッダー調整と、サーバー応答後のステータス判定・データ変換をチェーン形式で定義します。この構造により、後からロギングやレートリミッターなどのミドルウェアを挿入しやすくなります。

const createInterceptorPipeline = (chainHandlers) => {
  const preprocessRequest = (requestParams) => {
    requestParams.customHeaders = { ...requestParams.customHeaders };
    const appInstance = getApp();
    requestParams.customHeaders['Authorization'] = `Bearer ${appInstance.globalData.sessionToken}`;
    return chainHandlers.request ? chainHandlers.request(requestParams) : requestParams;
  };

  const postprocessResponse = (rawResponse) => {
    const { statusCode, data } = rawResponse;
    if (statusCode >= 200 && statusCode < 300) {
      if (data.userContext) {
        getApp().globalData.userContext = data.userContext;
      }
      return Promise.resolve(data);
    }
    const networkError = new Error(`通信異常: HTTP ${statusCode}`);
    networkError.originalPayload = rawResponse;
    return Promise.reject(networkError);
  };

  return {
    beforeSend: preprocessRequest,
    afterReceive: postprocessResponse
  };
};

export { createInterceptorPipeline };

グローバル状態の保持

認証情報やユーザープロファイルは、アプリケーション起動時に初期化された`App`インスタンスの`globalData`で管理します。これにより、ページ遷移やコンポーネントの再レンダリングでも状態が保持されます。

// app.js
App({
  globalData: {
    sessionToken: null,
    userContext: null
  }
});

実行フローと利用例

インターセプターで加工された設定を用いて通信を実行し、結果をチェーン処理で受け渡します。

import { apiGateway, createInterceptorPipeline } from './httpClient';

const middleware = createInterceptorPipeline({
  request: (cfg) => {
    console.log('[Interceptor] 送信準備:', cfg.targetUrl);
    return cfg;
  },
  response: (res) => {
    console.log('[Interceptor] 受信完了:', res.statusCode);
    return res;
  }
});

const executeWithPipeline = (requestConfig) => {
  const enrichedConfig = middleware.beforeSend(requestConfig);
  const methodKey = (enrichedConfig.httpMethod || 'GET').toLowerCase();
  return apiGateway[methodKey](enrichedConfig)
    .then(middleware.afterReceive);
};

// 呼び出し
executeWithPipeline({
  targetUrl: 'https://api.example.com/v1/accounts',
  httpMethod: 'GET'
})
.then(payload => console.log('レスポンスデータ:', payload))
.catch(failure => console.error('処理中断:', failure.message));

実装上の留意点

  • 処理順序は必ず「リクエストフェーズ → ネイティブ通信 → レスポンスフェーズ」となっており、この順序は実行環境により保証されます。
  • グローバル変数へのアクセスは`getApp()`を通じて行いますが、同期的に値が確定していないタイミングで呼び出すと`undefined`が返るため、初期化ロジックとの整合性を確認してください。
  • 引数オブジェクトはスプレッド構文で浅いコピーを行う設計にしており、元の設定がインターセプター内で参照透過性を保ちつつ拡張されます。
  • ベースURLやタイムアウト値は、必要に応じて`coreDispatcher`の呼び出し前にデフォルト値をマージするラッパーを別に定義するのが推奨されます。

タグ: WeChat Mini Program wx.request Interceptor Pattern Promises state management

5月28日 02:43 投稿