npm package: @nsrd/neuq-request
Description: a wrapper of axios for NeuqSoft RD Gateway.
Author: baiyx@neuqsoft
Latest version: 3.0.0-alpha.2

# Neuq Request

Neuq Request 为基于 [axios](https://github.com/axios/axios)
的 [河北东软统一服务网关](https://oa.neuqsoft.com/rdoa/docs/app-gateway/strict/gateway-api-define.html)
的 JS 网络请求实现。

## 前置条件
- `blob+sm4` 加密模式需要网关服务版本 `>= 3.1.0`
  - `"x-gateway-body": "blob"`, `encryptType: ENCRYPT_TYPE.SM4`同时存在

## 使用

1. 安装
   - pnpm(推荐)
     ```
     pnpm add @nsrd/neuq-request
     ```
   - npm
     ```
     npm i --save @nsrd/neuq-request
     ```
   - yarn
     ```
     yarn add @nsrd/neuq-request
     ```

2. 引用

   - import 引入

     ```javascript
     import { NeuqRequest, SIGN_TYPE, ENCRYPT_TYPE } from "@nsrd/neuq-request";
     ```
   - require 引入

     ```javascript
     const { NeuqRequest, SIGN_TYPE, ENCRYPT_TYPE } = require("@nsrd/neuq-request");
     ```
   - 对es6支持程度低的框架
     
     ```javascript
     import { NeuqRequest, SIGN_TYPE, ENCRYPT_TYPE } from "@nsrd/neuq-request/lib/neuq-request.es.js";
     ```
     ```javascript
     const { NeuqRequest, SIGN_TYPE, ENCRYPT_TYPE } = require("@nsrd/neuq-request/lib/neuq-request.cjs.js");
     ```
   - 网页直接使用
     
      ```html
       <script src="./lib/neuq-request.iife.js"></script>
       <scrip>
           var requestObj = window['@nsrd/neuq-request']
           var NeuqRequest = requestObj.NeuqRequest
           var SIGN_TYPE = requestObj.SIGN_TYPE
           var ENCRYPT_TYPE = requestObj.ENCRYPT_TYPE
       </scrip>
      ```
   - 小程序（支付宝/微信）

      ```javascript
      import { NeuqRequest, SIGN_TYPE, ENCRYPT_TYPE } from "@nsrd/neuq-request/lib/neuq-request.mini.es.js";
      ```

3. 创建实例

    ```javascript
    const http = NeuqRequest(options<Object>);
    ```

   实例选项 options：

  - `baseURL<String>` 根地址，选填项，默认值 `/`
  - `adapter<Function(config<Object>)>` 自定义处理请求, 选填项，请参考[axios/lib/adapters](https://github.com/axios/axios/blob/v1.x/lib/adapters/README.md)
  - `timeout<String>`，网络请求超时时间，单位毫秒，选填项，默认值 `6000`
  - `headers<Object>`，请求头，选填项，默认值 `{}`
      - headers 包含 `"C-GATEWAY-QUERY-ENCRYPT": "1"`
        对 url参数 `params` 进行加密处理，此时 `securityGateway` 和 `securityGateway.encryptType` 为必填项
      - headers 包含 `"x-gateway-body": "json"` (加密默认该模式)
        对请求与响应进行json加密与传输
      - headers 包含 `"x-gateway-body": "blob"` 
        对请求与响应进行二进制加密与传输
  - `requestInterceptor<Function(config<Object>)>`
    ，请求拦截器，选填项，请参考 [Aixos request interceptor](https://github.com/axios/axios#interceptors)
  - `responseSuccessInterceptor<Function(response<Object>)>`
    ，响应成功拦截器，选填项，请参考 [Aixos response interceptor](https://github.com/axios/axios#interceptors)
  - `responseFailInterceptor<Function(error<Object>)>`
    ，响应失败拦截器，选填项，请参考 [Aixos response interceptor](https://github.com/axios/axios#interceptors)
  - `securityGateway<Object | String>`，安全网关选项，支持JSON对象与字符两种格式，选填项
    - `Object` JSON对象
      - `appId`
        ，必填项，请参考 [河北东软统一服务网关-公共参数](https://oa.neuqsoft.com/rdoa/docs/app-gateway/strict/gateway-api-define.html)
      - `signType`，选填项，可选值，默认值 `SIGN_TYPE.SM2`
        ，请参考 [河北东软统一服务网关-公共参数](https://oa.neuqsoft.com/rdoa/docs/app-gateway/strict/gateway-api-define.html)
      - `encryptType`，选填项，可选值，默认值 `ENCRYPT_TYPE.NONE`
        ，请参考 [河北东软统一服务网关-公共参数](https://oa.neuqsoft.com/rdoa/docs/app-gateway/strict/gateway-api-define.html)
      - `appSignPrivateKey`，应用系统签名私钥，必填项
      - `appSignPublicKey`，应用系统签名公钥，必填项
      - `encryptKey`，应用系统与网关约定的加密秘钥，如 `encryptType` 不为 `ENCRYPT_TYPE.NONE` 则为必填项
      - `platformPublicKey`，网关公钥，必填项
      - `platformPublicKey`，网关公钥，必填项
    - `String` 字符串，该参数生成自 `网关管理-加密工具(gateway-manager/#/gateway/utils)`

   以使用 SM2 签名、SM4 加密方式构造请求实例：
  - 二进制加密传输，加密参数为JSON对象

   ```javascript
    const http = NeuqRequest({
        baseURL: "./",
        timeout: 10 * 1000,
        headers: {
            "C-GATEWAY-QUERY-ENCRYPT": "1",
            "x-gateway-body": "blob"
        },
        requestInterceptor: function (config) {
            return config
        },
        responseSuccessInterceptor: function (response) {
            // 解析示例，请根据实际返回进行修改
            return JSON.parse(response.data.body)
        },
        responseFailInterceptor: function (error) {
            return error
        },
        securityGateway: {
          appId: "DemoSM2",
          signType: SIGN_TYPE.SM2,
          encryptType: ENCRYPT_TYPE.SM4,
          appSignPrivateKey: "......",
          appSignPublicKey: "......",
          encryptKey: "......",
          platformPublicKey: "......"
        }
    });
   ```
  - json加密传输，加密参数为JSON对象

   ```javascript
    const http = NeuqRequest({
      baseURL: "./",
      timeout: 10 * 1000,
      headers: {
        "C-GATEWAY-QUERY-ENCRYPT": "1",
        "x-gateway-body": "json"
      },
      requestInterceptor: function (config) {
         return config
      },
      responseSuccessInterceptor: function (response) {
        // 解析示例，请根据实际返回进行修改
        return JSON.parse(response.data.body)
      },
      responseFailInterceptor: function (error) {
        return error
      },
      securityGateway: {
        appId: "DemoSM2",
        signType: SIGN_TYPE.SM2,
        encryptType: ENCRYPT_TYPE.SM4,
        appSignPrivateKey: "......",
        appSignPublicKey: "......",
        encryptKey: "......",
        platformPublicKey: "......"
      }
    });
   ```

  - 二进制加密传输，加密参数为字符串

  ```javascript
    const http = NeuqRequest({
      baseURL: "./",
      timeout: 10 * 1000,
      headers: {
        "C-GATEWAY-QUERY-ENCRYPT": "1",
        "x-gateway-body": "blob"
      },
      requestInterceptor: function (config) {
        return config
      },
      responseSuccessInterceptor: function (response) {
        // 解析示例，请根据实际返回进行修改
        return JSON.parse(response.data.body)
      },
      responseFailInterceptor: function (error) {
        return error
      },
      securityGateway: "f270994f35b5c8ee99c249af8fb022c3c3b8a7e7507d24383f22c3db8d765c45dfe93721af24a6b9ce6c92b......"
    });
  ```

  - 普通网络请求

  ```javascript
   const http = NeuqRequest({
     baseURL: "./",
     timeout: 10 * 1000,
     headers: {
       "Content-Type": "application/json"
     },
     requestInterceptor: function(config) {
       return config;
     },
     responseSuccessInterceptor: function(response) {
       return response.data;
     },
     responseFailInterceptor: function(error) {
       return error.response;
     }
   });
  ```
4. 节流器

- `generationThrottlingAdapter<Function<time<Number>>>` 节流器构造器, 参数`time`为毫秒，默认节流时间为200ms
- 对节流时间内的**同url、同params、同data(JSON格式对象)、同headers**的请求进行拦截，只发送一次实际网络请求，得到结果后进行分发

```javascript
import { NeuqRequest, generationThrottlingAdapter } from "@nsrd/neuq-request";

const http = NeuqRequest({
    baseURL: "./",
    adapter: generationThrottlingAdapter(200),
    timeout: 10 * 1000,
    headers: {
      "C-GATEWAY-QUERY-ENCRYPT": "1",
      "x-gateway-body": "blob"
    },
    requestInterceptor: function (config) {
      return config
    },
    responseSuccessInterceptor: function (response) {
      // 解析示例，请根据实际返回进行修改
      return JSON.parse(response.data.body)
    },
    responseFailInterceptor: function (error) {
      return error
    },
    securityGateway: "f270994f35b5c8ee99c249af8fb022c3c3b8a7e7507d24383f22c3db8d765c45dfe93721af24a6b9ce6c92b......"
  });
```


5. 网络请求

   当前只支持 `POST`/`DELETE`/`PUT`/`GET` 四种请求方式。

   <font color=#ff0000>安全性建议：只使用 'POST' 与 'GET' 两种请求</font>


  - post `http.post(config<Object>)`
  - delete `http.delete(config<Object>)`
  - put `http.put(config<Object>)`
  - get `http.get(config<Object>)`

   选项 config，与 [Axios request config](https://github.com/axios/axios#request-config) 一致。

   以使用 SM2 签名、SM4 加密方式构造的请求实例发送 post 请求：

   ```javascript
   http
    .post({
      url: "gateway-request/demo/mock/390/json",
      params: {
        name: '张三',
        id: 'QWERTYU1234'
      },
      data: {
        bizNo: "GO20300258433171456"
      }
    })
    .then(res => {
      console.log("handleSM2SM4PostRequest", res);
    })
    .catch(err => {
      console.error("handleSM2SM4PostRequest", err);
    })
    .finally(() => {});
   ```
  ```javascript
   http({
      method: "post",
      url: "gateway-request/demo/mock/390/json",
      params: {
        name: '张三',
        id: 'QWERTYU1234'
      },
      data: {
        bizNo: "GO20300258433171456"
      }
    })
    .then(res => {
      console.log("handleSM2SM4PostRequest", res);
    })
    .catch(err => {
      console.error("handleSM2SM4PostRequest", err);
    })
    .finally(() => {});
  ```

## 内置签名与加密工具

```javascript
import {SM, RSA, AES} from "@nsrd/neuq-request";
```

1. sm2 签名 `SM.sm2.signature(message, appSignPrivateKey, appSignPublicKey, appId);`
2. sm4 加密 `SM.sm4.encrypt(message, encryptKey);`
3. sm4 解密 `SM.sm4.decrypt(message, encryptKey);`
4. rsa 签名 `RSA.signature(message, appSignPrivateKey, rsa.alg.SHA256withRSA);`
5. ase 加密 `AES.encrypt(message, encryptKey);`
6. ase 解密 `AES.decrypt(message, encryptKey);`

## 更新计划

- [x] 支持 AES 加密与解密
- [ ] 支持文件上传

## 依赖项

- 所有放置于devDependencies依赖项，打包发布后属于非必须安装部分

## 代码提交
- feat：提交新功能

- fix：修复了bug

- docs：只修改了文档

- style：调整代码格式，未修改代码逻辑（比如修改空格、格式化、缺少分号等）

- refactor：代码重构，既没修复bug也没有添加新功能

- perf：性能优化，提高性能的代码更改

- test：添加或修改代码测试

- chore：对构建流程或辅助工具和依赖库（如文档生成等）的更改

如有**异常**或**需求**，请联系 [baiyx](mailto:baiyx@neuqsoft.com) 。