如何设置网络代理
========

#### 问题现象

如何为网络请求添加代理配置。

#### 背景知识

*   [connection.setAppHttpProxy](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-net-connection#connectionsetapphttpproxy11)：设置应用级Http代理配置信息。
*   [connection.getDefaultHttpProxy](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-net-connection#connectiongetdefaulthttpproxy10)：获取网络的默认代理配置信息。
*   [ProxyConfiguration](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/remote-communication-rcp#proxyconfiguration)：ProxyConfiguration接口允许开发者为会话中的HTTP请求配置代理设置，从而提供在系统、自定义或无代理之间进行选择的灵活性。
*   [HttpProxy](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-net-connection#httpproxy10)：网络代理配置信息。

#### 解决方案

权限声明，module.json5中配置权限声明：

使用权限：ohos.permission.INTERNET。

*   方案一：
    
    使用connection.setAppHttpProxy接口设置应用级别代理配置信息，会覆盖系统设置的代理配置信息。配置后应用内网络请求如果没有额外配置代理信息，默认执行该方法配置的代理信息。取消代理配置时，使用connection.setAppHttpProxy把host设置为空字符串，port设置为0。
    
    async setProxyStatus(host: string, port: number) {
      this.isLoading = true;
      try {
        connection.setAppHttpProxy({
          host: host,
          port: port,
          exclusionList: \['XXX'\]
        } as connection.HttpProxy);
        this.checkProxyStatus();
      } catch (err) {
        this.proxyEnabled = false;
        this.proxyInfo = \`设置失败：${JSON.stringify(err)}\`;
      } finally {
        this.isLoading = false;
      }
    }
    
    connection.getDefaultHttpProxy接口获取网络的默认代理配置信息，如果设置了全局代理，则返回全局代理配置信息。可以通过获取到的信息中host是否为空，port是否大于0，判断应用当前是否开启了网络代理。
    
    async checkProxyStatus() {
      this.isLoading = true;
      this.proxyInfo = '检测中...';
      try {
       _ // 获取全局代理详细信息_
        let proxyDetail = await connection.getDefaultHttpProxy();
    
    
        if (proxyDetail.host.length > 0 && proxyDetail.port > 0) {
          this.proxyEnabled = true;
          this.proxyInfo = \`${proxyDetail.host}:${proxyDetail.port}\`;
        } else {
          this.proxyEnabled = false;
          this.proxyInfo = \`...\`;
        }
      } catch (err) {
        this.proxyEnabled = false;
        this.proxyInfo = \`检测失败：${JSON.stringify(err)}\`;
      } finally {
        this.isLoading = false;
      }
    }
    
*   方案二：
    
    为单次网络请求配置代理信息。
    
    httpRequest.request请求时，HttpRequestOptions中的usingProxy代理信息可以自定义设置，无代理或系统设置的代理配置。
    
    async testHttpProxyRequest() {
      this.isLoading = true;
      this.testResult = '请求中...';
      try {
        const httpRequest = http.createHttp();
        const response = await httpRequest.request(this.url,
          {
            usingProxy: {
              host: this.proxyUrl,
              port: this.proxyPort
            } as connection.HttpProxy
          }
        );
        if (response.responseCode === 200) {
          const result = JSON.stringify(response.result);
          this.testResult = \`请求成功 .\\n：${result}\`;
        } else {
          this.testResult = \`请求失败 .\\n响应码：${response.responseCode}\`;
        }
      } catch (err) {
        this.testResult = \`请求异常 .\\n错误信息：${JSON.stringify(err)}\`;
      } finally {
        this.isLoading = false;
      }
    }
    
    rcp请求时，rcp.Configuration中的proxy代理信息允许开发者为会话中的HTTP请求配置代理设置，从而提供在系统、自定义或无代理之间进行选择的灵活性。
    
    async testRcpProxyRequest() {
      this.isLoading = true;
      this.testResult = '请求中...';
      try {
        const session = rcp.createSession();
      _  // 自定义proxy_
        const configuration: rcp.Configuration = {
          proxy: {
            url: \`${this.proxyUrl}:${this.proxyPort}\`,
            createTunnel: 'always',
          }
        };
        const request = new rcp.Request(this.url, 'GET');
        request.configuration = configuration;
    
    
        session.fetch(request).then((response: rcp.Response) => {
          if (response.statusCode === 200) {
            this.testResult = \`请求成功 .\\n：${response}\`;
          } else {
            this.testResult = \`请求失败 .\\n响应码：${response.statusCode}\`;
          }
          session.close();
        }).catch((err: BusinessError) => {
          console.error(\`The error code is ${err.code}, error message is ${JSON.stringify(err)}\`);
          session.close();
        });
      } catch (err) {
        this.testResult = \`请求异常 .\\n错误信息：${JSON.stringify(err)}\`;
      } finally {
        this.isLoading = false;
      }
    }
    
*   完整代码：
    
    import { connection } from '@kit.NetworkKit';
    import http from '@ohos.net.http';
    import { webview } from '@kit.ArkWeb';
    import { rcp } from '@kit.RemoteCommunicationKit';
    
    
    @Entry
    @Component
    struct ProxyCheckPage {
      @State proxyEnabled: boolean = false; _// 是否启用代理_
      url: string = 'XXX.XXX.XXX'; _// 需要替换为开发者需要的请求地址_
      proxyUrl: string = 'XXX.XXX.XXX.XXX'; _// 需要替换为开发者需要的代理地址_
      proxyPort: number = 8888; _// 需要替换为开发者需要的代理端口_
      @State proxyInfo: string = '未检测'; _// 代理信息文本_
      @State testResult: string = '未测试'; _// 测试请求结果_
      @State isLoading: boolean = false; _// 加载状态_
      webviewController: webview.WebviewController = new webview.WebviewController();
    
    
      aboutToAppear(): void {
        this.checkProxyStatus();
      }
    
    
      @Styles
      fancy() {
        .width('80%')
        .height(50)
        .enabled(!this.isLoading)
        .margin({ bottom: 15 });
      }
    
    
      build() {
        Column() {
        _  // 检测代理按钮_
          Button('一键检测代理状态')
            .fancy()
            .backgroundColor('#007DFF')
            .fontColor(Color.White)
            .onClick(() => this.checkProxyStatus());
    
    
        _  // 设置代理按钮_
          Button('设置应用级代理')
            .fancy()
            .backgroundColor('#007DFF')
            .fontColor(Color.White)
            .onClick(() => this.setProxyStatus(this.proxyUrl, this.proxyPort));
    
    
         _ // 取消代理按钮_
          Button('取消应用级代理')
            .fancy()
            .backgroundColor('#007DFF')
            .fontColor(Color.White)
            .onClick(() => this.setProxyStatus('', 0));
    
    
          Text(\`代理启用状态：${this.proxyEnabled ? '已启用' : '未启用'}\`)
            .fontSize(16)
            .margin({ bottom: 20 });
    
    
       _   // 代理详细信息展示_
          Text('代理详细信息：')
            .fontSize(18)
            .fontWeight(FontWeight.Medium)
            .alignSelf(ItemAlign.Start)
            .margin({ left: 40, bottom: 10 });
    
    
          Text(this.proxyInfo)
            .fontSize(14)
            .width('80%')
            .backgroundColor('#F5F7FA')
            .padding(15)
            .borderRadius(8)
            .margin({ bottom: 20 })
            .textAlign(TextAlign.Start);
    
    
      _    // 测试请求按钮_
          Button('发起测试请求验证代理')
            .fancy()
            .backgroundColor('#36CFC9')
            .fontColor(Color.White)
            .onClick(() => this.testProxyRequest());
    
    
        _  // http测试请求_
          Button('http请求设置代理')
            .fancy()
            .backgroundColor('#36CFC9')
            .fontColor(Color.White)
            .onClick(() => this.testHttpProxyRequest());
    
    
       _   //rcp测试请求按钮_
          Button('rcp测试请求设置代理')
            .fancy()
            .backgroundColor('#36CFC9')
            .fontColor(Color.White)
            .onClick(() => this.testRcpProxyRequest());
    
    
          Text('测试请求结果：')
            .fontSize(18)
            .fontWeight(FontWeight.Medium)
            .alignSelf(ItemAlign.Start)
            .margin({ left: 40, bottom: 10 });
    
    
          Text(this.testResult)
            .fontSize(14)
            .width('80%')
            .backgroundColor('#F5F7FA')
            .padding(15)
            .borderRadius(8)
            .textAlign(TextAlign.Start);
        }
        .width('100%')
        .height('100%')
        .padding(20)
        .backgroundColor(Color.White)
        .justifyContent(FlexAlign.Center);
      }
    
    
      async checkProxyStatus() {
        this.isLoading = true;
        this.proxyInfo = '检测中...';
        try {
       _   // 获取全局代理详细信息_
          let proxyDetail = await connection.getDefaultHttpProxy();
    
    
          if (proxyDetail.host.length > 0 && proxyDetail.port > 0) {
            this.proxyEnabled = true;
            this.proxyInfo = \`${proxyDetail.host}:${proxyDetail.port}\`;
          } else {
            this.proxyEnabled = false;
            this.proxyInfo = \`...\`;
          }
        } catch (err) {
          this.proxyEnabled = false;
          this.proxyInfo = \`检测失败：${JSON.stringify(err)}\`;
        } finally {
          this.isLoading = false;
        }
      }
    
    
      async setProxyStatus(host: string, port: number) {
        this.isLoading = true;
        try {
          connection.setAppHttpProxy({
            host: host,
            port: port,
            exclusionList: \['XXX'\]
          } as connection.HttpProxy);
          this.checkProxyStatus();
        } catch (err) {
          this.proxyEnabled = false;
          this.proxyInfo = \`设置失败：${JSON.stringify(err)}\`;
        } finally {
          this.isLoading = false;
        }
      }
    
    
      async testProxyRequest() {
        this.isLoading = true;
        this.testResult = '请求中...';
        try {
          const httpRequest = http.createHttp();
          const response = await httpRequest.request(this.url);
          if (response.responseCode === 200) {
            const result = JSON.stringify(response.result);
            this.testResult = \`请求成功 .\\n：${result}\`;
          } else {
            this.testResult = \`请求失败 .\\n响应码：${response.responseCode}\`;
          }
        } catch (err) {
          this.testResult = \`请求异常 .\\n错误信息：${JSON.stringify(err)}\`;
        } finally {
          this.isLoading = false;
        }
      }
    
    
      async testHttpProxyRequest() {
        this.isLoading = true;
        this.testResult = '请求中...';
        try {
          const httpRequest = http.createHttp();
          const response = await httpRequest.request(this.url,
            {
              usingProxy: {
                host: this.proxyUrl,
                port: this.proxyPort
              } as connection.HttpProxy
            }
          );
          if (response.responseCode === 200) {
            const result = JSON.stringify(response.result);
            this.testResult = \`请求成功 .\\n：${result}\`;
          } else {
            this.testResult = \`请求失败 .\\n响应码：${response.responseCode}\`;
          }
        } catch (err) {
          this.testResult = \`请求异常 .\\n错误信息：${JSON.stringify(err)}\`;
        } finally {
          this.isLoading = false;
        }
      }
      async testRcpProxyRequest() {
        this.isLoading = true;
        this.testResult = '请求中...';
        try {
          const session = rcp.createSession();
      _    // 自定义proxy_
          const configuration: rcp.Configuration = {
            proxy: {
              url: \`${this.proxyUrl}:${this.proxyPort}\`,
              createTunnel: 'always',
            }
          };
          const request = new rcp.Request(this.url, 'GET');
          request.configuration = configuration;
    
    
          session.fetch(request).then((response: rcp.Response) => {
            if (response.statusCode === 200) {
              this.testResult = \`请求成功 .\\n：${response}\`;
            } else {
              this.testResult = \`请求失败 .\\n响应码：${response.statusCode}\`;
            }
            session.close();
          }).catch((err: BusinessError) => {
            console.error(\`The error code is ${err.code}, error message is ${JSON.stringify(err)}\`);
            session.close();
          });
        } catch (err) {
          this.testResult = \`请求异常 .\\n错误信息：${JSON.stringify(err)}\`;
        } finally {
          this.isLoading = false;
        }
      }
    }