HaniBlindBox/docs/前端适配调整/request.js修改方案.md
2026-01-03 15:21:36 +08:00

7.2 KiB
Raw Blame History

request.js 修改方案

1. 核心修改

1.1 修改 Content-Type

将 POST 请求的 Content-Type 从 application/x-www-form-urlencoded 改为 application/json

1.2 修改位置

文件:honey_box/common/request.js

找到以下代码块(约第 130-140 行):

if (method.toUpperCase() == 'POST') {
    // ... 签名计算代码 ...

    header = {
        'content-type': 'application/x-www-form-urlencoded',
        client: client,
        token: token,
        adid: uni.getStorageSync('_ad_id'),
        clickid: uni.getStorageSync('_click_id')
    }
}

1.3 修改后代码

if (method.toUpperCase() == 'POST') {
    // ... 签名计算代码保持不变 ...

    header = {
        'content-type': 'application/json',  // 修改这一行
        client: client,
        token: token,
        adid: uni.getStorageSync('_ad_id'),
        clickid: uni.getStorageSync('_click_id')
    }
}

2. 完整修改后的 request.js

/**
 * 网络请求工具类
 * 封装统一的网络请求方法
 */

import EnvConfig from '@/common/env.js'
import md5 from 'js-md5'
 
import { apiWhiteList } from '@/common/config.js'
import RouterManager from '@/common/router.js'
import { platform } from '@/common/platform/PlatformFactory'

class RequestManager {
  // 缓存对象
  static cache = {
    data: new Map(),
    expireTime: 5 * 60 * 1000,
    timestamps: new Map()
  };

  static isCacheValid(cacheKey) {
    const now = Date.now();
    if (this.cache.data.has(cacheKey)) {
      const timestamp = this.cache.timestamps.get(cacheKey);
      return now - timestamp < this.cache.expireTime;
    }
    return false;
  }

  static updateCache(cacheKey, data) {
    this.cache.data.set(cacheKey, data);
    this.cache.timestamps.set(cacheKey, Date.now());
  }

  static getCache(url, data = {}, showLoading = false) {
    const cacheKey = url + JSON.stringify(data);
    if (this.isCacheValid(cacheKey)) {
      console.log(cacheKey + '缓存有效');
      return Promise.resolve(this.cache.data.get(cacheKey));
    }
    return this.get(url, data, showLoading).then(result => {
      this.updateCache(cacheKey, result);
      return result;
    });
  }

  static isUrlInWhitelist(url) {
    return apiWhiteList.some(whiteItem => url.indexOf(whiteItem) > -1);
  }

  static generateNonce() {
    return md5(Date.now() + Math.random().toString(36).substring(2, 15));
  }

  static request(param, backpage, backtype) {
    return new Promise((resolve, reject) => {
      if (!param || typeof param !== 'object') {
        reject(new Error('请求参数错误'))
        return
      }

      uni.getNetworkType({
        success: function (res) {
          if (res.networkType == 'none') {
            uni.showToast({
              title: '网络连接异常,请检查网络',
              icon: 'none'
            })
            reject(new Error('网络连接异常'))
            return
          }
        }
      })

      const url = param.url || ''
      const method = param.method || 'POST'
      const data = param.data || {}
      const Loading = param.Loading || false
      const token = uni.getStorageSync('token')
      let client = platform.code
      const apiBaseUrl = EnvConfig.apiBaseUrl

      let requestUrl = ''
      if (url.startsWith('http://') || url.startsWith('https://')) {
        requestUrl = url
      } else {
        const baseUrlWithSlash = apiBaseUrl.endsWith('/') ? apiBaseUrl : apiBaseUrl + '/'
        const path = url.startsWith('/') ? url.substring(1) : url
        requestUrl = baseUrlWithSlash + path
      }

      const hostRegex = /^(?:https?:\/\/)?([^\/]+)/i
      const matches = requestUrl.match(hostRegex)
      const host = matches && matches[1] ? matches[1] : 'localhost'

      let header = {}

      // 添加签名参数
      data.timestamp = Math.floor(Date.now() / 1000)
      data.nonce = RequestManager.generateNonce()

      // 统一签名计算逻辑
      const sortedParams = {}
      Object.keys(data).sort().forEach(key => {
        sortedParams[key] = data[key]
      })

      let signStr = ''
      for (const key in sortedParams) {
        if (typeof sortedParams[key] === 'object') {
          signStr += key + '=' + JSON.stringify(sortedParams[key]) + '&'
        } else {
          signStr += key + '=' + sortedParams[key] + '&'
        }
      }

      const timestamp = data.timestamp
      const appSecret = host + timestamp
      signStr = signStr.substring(0, signStr.length - 1) + appSecret
      const sign = md5(signStr)
      data.sign = sign

      // 统一使用 JSON 格式的请求头
      header = {
        'content-type': 'application/json',  // 关键修改:统一使用 JSON
        client: client,
        token: token,
        adid: uni.getStorageSync('_ad_id'),
        clickid: uni.getStorageSync('_click_id')
      }

      if (!Loading) {
        uni.showLoading({
          title: '加载中...'
        })
      }

      uni.request({
        url: requestUrl,
        method: method.toUpperCase(),
        header: header,
        data: data,
        success: res => {
          // ... 响应处理代码保持不变 ...
        },
        fail: e => {
          console.error('网络请求失败:', e)
          uni.showToast({
            title: e.errMsg || '请求失败',
            icon: 'none'
          })
          typeof param.fail == 'function' && param.fail(e)
          reject(e)
        },
        complete: () => {
          uni.hideLoading()
          typeof param.complete == 'function' && param.complete()
        }
      })
    })
  }

  static get(url, data = {}, showLoading = true) {
    return this.request({
      url,
      data,
      method: 'GET',
      Loading: !showLoading
    })
  }

  static post(url, data = {}, showLoading = true) {
    return this.request({
      url,
      data,
      method: 'POST',
      Loading: !showLoading
    })
  }
}

export default RequestManager;

3. 签名机制说明

修改 Content-Type 后,签名机制保持不变:

  1. 参数按键名排序
  2. 拼接为 key=value& 格式
  3. 添加密钥 host + timestamp
  4. MD5 加密生成签名

后端需要确保能正确解析 JSON 格式的请求体并验证签名。

4. 后端签名验证

C# 后端需要从 JSON 请求体中提取参数进行签名验证:

// 示例:签名验证中间件
public class SignatureValidationMiddleware
{
    public async Task InvokeAsync(HttpContext context)
    {
        if (context.Request.Method == "POST" && 
            context.Request.ContentType?.Contains("application/json") == true)
        {
            // 读取请求体
            context.Request.EnableBuffering();
            var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
            context.Request.Body.Position = 0;
            
            // 解析 JSON 并验证签名
            var data = JsonSerializer.Deserialize<Dictionary<string, object>>(body);
            // ... 签名验证逻辑 ...
        }
        
        await _next(context);
    }
}

5. 测试验证

修改后需要测试以下场景:

  1. 登录接口POST /api/login
  2. 获取用户信息POST /api/user
  3. 商品列表POST /api/goods_list
  4. 创建订单POST /api/orderbuy

确保签名验证通过,数据正确传递。