Harmony之路:网络请求——HTTP模块与数据交互

Harmony之路:网络请求——HTTP模块与数据交互

一、引入:为什么需要网络请求?

在现代移动应用中,几乎所有的应用都需要与服务器进行数据交互,无论是获取新闻列表、用户登录、上传图片还是同步数据,都离不开网络请求。HarmonyOS提供了HTTP模块作为网络请求的核心能力,支持GET、POST、PUT、DELETE等多种HTTP方法,能够满足各种网络通信需求。掌握HTTP模块的使用,是构建完整应用的必要技能。

二、讲解:HTTP模块的核心用法

1. HTTP模块基础配置

在使用HTTP模块前,需要先在应用的配置文件中声明网络权限。

config.json权限配置:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

2. 创建HTTP请求

创建HTTP请求示例:

import http from '@ohos.net.http';

// 创建HTTP请求对象
const httpRequest = http.createHttp();

// 设置请求超时时间(可选)
httpRequest.setTimeout(10000); // 10秒超时

3. GET请求

GET请求用于从服务器获取数据,是最常用的请求方法。

GET请求示例:

import http from '@ohos.net.http';

@Entry
@Component
struct GetExample {
  @State data: string = '';

  async fetchData() {
    try {
      const httpRequest = http.createHttp();
      
      // 发起GET请求
      const response = await httpRequest.request(
        'https://jsonplaceholder.typicode.com/posts/1',
        {
          method: http.RequestMethod.GET,
          header: {
            'Content-Type': 'application/json'
          }
        }
      );

      // 检查响应状态
      if (response.responseCode === 200) {
        const result = JSON.parse(response.result);
        this.data = JSON.stringify(result, null, 2);
        console.log('请求成功:', result);
      } else {
        console.error('请求失败:', response.responseCode);
        this.data = `请求失败: ${response.responseCode}`;
      }
    } catch (error) {
      console.error('请求异常:', error);
      this.data = `请求异常: ${error.message}`;
    }
  }

  build() {
    Column({ space: 20 }) {
      Button('发起GET请求')
        .onClick(() => {
          this.fetchData();
        })
      
      Text(this.data)
        .fontSize(14)
        .textAlign(TextAlign.Start)
        .width('90%')
        .height(300)
        .border({ width: 1, color: Color.Gray })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

4. POST请求

POST请求用于向服务器提交数据,常用于创建资源或提交表单。

POST请求示例:

import http from '@ohos.net.http';

@Entry
@Component
struct PostExample {
  @State result: string = '';

  async submitData() {
    try {
      const httpRequest = http.createHttp();
      
      // 请求数据
      const requestData = {
        title: 'foo',
        body: 'bar',
        userId: 1
      };

      // 发起POST请求
      const response = await httpRequest.request(
        'https://jsonplaceholder.typicode.com/posts',
        {
          method: http.RequestMethod.POST,
          header: {
            'Content-Type': 'application/json'
          },
          extraData: JSON.stringify(requestData)
        }
      );

      if (response.responseCode === 201) {
        const result = JSON.parse(response.result);
        this.result = JSON.stringify(result, null, 2);
        console.log('创建成功:', result);
      } else {
        console.error('创建失败:', response.responseCode);
        this.result = `创建失败: ${response.responseCode}`;
      }
    } catch (error) {
      console.error('请求异常:', error);
      this.result = `请求异常: ${error.message}`;
    }
  }

  build() {
    Column({ space: 20 }) {
      Button('发起POST请求')
        .onClick(() => {
          this.submitData();
        })
      
      Text(this.result)
        .fontSize(14)
        .textAlign(TextAlign.Start)
        .width('90%')
        .height(300)
        .border({ width: 1, color: Color.Gray })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

5. PUT和DELETE请求

PUT用于更新资源,DELETE用于删除资源。

PUT和DELETE请求示例:

import http from '@ohos.net.http';

class ApiService {
  private httpRequest: http.HttpRequest;

  constructor() {
    this.httpRequest = http.createHttp();
  }

  // 更新资源
  async updatePost(id: number, data: any) {
    try {
      const response = await this.httpRequest.request(
        `https://jsonplaceholder.typicode.com/posts/${id}`,
        {
          method: http.RequestMethod.PUT,
          header: {
            'Content-Type': 'application/json'
          },
          extraData: JSON.stringify(data)
        }
      );

      if (response.responseCode === 200) {
        return JSON.parse(response.result);
      } else {
        throw new Error(`更新失败: ${response.responseCode}`);
      }
    } catch (error) {
      throw error;
    }
  }

  // 删除资源
  async deletePost(id: number) {
    try {
      const response = await this.httpRequest.request(
        `https://jsonplaceholder.typicode.com/posts/${id}`,
        {
          method: http.RequestMethod.DELETE
        }
      );

      if (response.responseCode === 200) {
        return { success: true };
      } else {
        throw new Error(`删除失败: ${response.responseCode}`);
      }
    } catch (error) {
      throw error;
    }
  }
}

6. 请求头设置

设置请求头可以传递认证信息、内容类型等。

请求头设置示例:

import http from '@ohos.net.http';

async function requestWithHeaders() {
  const httpRequest = http.createHttp();
  
  const response = await httpRequest.request(
    'https://api.example.com/data',
    {
      method: http.RequestMethod.GET,
      header: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer your-token-here',
        'User-Agent': 'MyApp/1.0',
        'Accept': 'application/json'
      }
    }
  );

  return response;
}

7. 请求参数

GET请求可以通过URL参数传递数据,POST请求可以通过extraData传递数据。

URL参数示例:

import http from '@ohos.net.http';

async function requestWithParams() {
  const httpRequest = http.createHttp();
  
  // 构建带参数的URL
  const params = new URLSearchParams({
    page: '1',
    limit: '10',
    sort: 'desc'
  });
  
  const url = `https://api.example.com/posts?${params.toString()}`;
  
  const response = await httpRequest.request(
    url,
    {
      method: http.RequestMethod.GET,
      header: {
        'Content-Type': 'application/json'
      }
    }
  );

  return response;
}

8. 文件上传

使用FormData格式上传文件。

文件上传示例:

import http from '@ohos.net.http';
import fileio from '@ohos.fileio';

async function uploadFile(filePath: string) {
  const httpRequest = http.createHttp();
  
  // 读取文件内容
  const fileContent = await fileio.readText(filePath);
  
  // 构建FormData(简化版,实际需要更复杂的处理)
  const formData = `------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain

${fileContent}
------WebKitFormBoundary7MA4YWxkTrZu0gW--`;

  const response = await httpRequest.request(
    'https://api.example.com/upload',
    {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
      },
      extraData: formData
    }
  );

  return response;
}

9. 实际应用场景

场景1:用户登录

import http from '@ohos.net.http';
import preferences from '@ohos.data.preferences';

class AuthService {
  private httpRequest: http.HttpRequest;
  private preferences: preferences.Preferences;

  constructor(context: common.UIAbilityContext) {
    this.httpRequest = http.createHttp();
    this.preferences = preferences.getPreferencesSync(context, 'authData');
  }

  async login(username: string, password: string): Promise<boolean> {
    try {
      const response = await this.httpRequest.request(
        'https://api.example.com/auth/login',
        {
          method: http.RequestMethod.POST,
          header: {
            'Content-Type': 'application/json'
          },
          extraData: JSON.stringify({ username, password })
        }
      );

      if (response.responseCode === 200) {
        const result = JSON.parse(response.result);
        
        // 保存token和用户信息
        await this.preferences.put('token', result.token);
        await this.preferences.put('userInfo', JSON.stringify(result.user));
        
        return true;
      } else {
        throw new Error('登录失败');
      }
    } catch (error) {
      console.error('登录异常:', error);
      return false;
    }
  }

  async logout() {
    try {
      const token = await this.preferences.get('token', '');
      
      if (token) {
        await this.httpRequest.request(
          'https://api.example.com/auth/logout',
          {
            method: http.RequestMethod.POST,
            header: {
              'Authorization': `Bearer ${token}`
            }
          }
        );
      }
      
      // 清除本地数据
      await this.preferences.delete('token');
      await this.preferences.delete('userInfo');
    } catch (error) {
      console.error('退出登录异常:', error);
    }
  }

  async getToken(): Promise<string> {
    return await this.preferences.get('token', '');
  }
}

场景2:获取列表数据

import http from '@ohos.net.http';

class DataService {
  private httpRequest: http.HttpRequest;

  constructor() {
    this.httpRequest = http.createHttp();
  }

  async getPosts(page: number = 1, limit: number = 10): Promise<any[]> {
    try {
      const params = new URLSearchParams({
        page: page.toString(),
        limit: limit.toString()
      });
      
      const url = `https://jsonplaceholder.typicode.com/posts?${params.toString()}`;
      
      const response = await this.httpRequest.request(
        url,
        {
          method: http.RequestMethod.GET,
          header: {
            'Content-Type': 'application/json'
          }
        }
      );

      if (response.responseCode === 200) {
        return JSON.parse(response.result);
      } else {
        throw new Error(`获取数据失败: ${response.responseCode}`);
      }
    } catch (error) {
      console.error('获取数据异常:', error);
      throw error;
    }
  }

  async getPostDetail(id: number): Promise<any> {
    try {
      const response = await this.httpRequest.request(
        `https://jsonplaceholder.typicode.com/posts/${id}`,
        {
          method: http.RequestMethod.GET,
          header: {
            'Content-Type': 'application/json'
          }
        }
      );

      if (response.responseCode === 200) {
        return JSON.parse(response.result);
      } else {
        throw new Error(`获取详情失败: ${response.responseCode}`);
      }
    } catch (error) {
      console.error('获取详情异常:', error);
      throw error;
    }
  }
}

场景3:带认证的请求

import http from '@ohos.net.http';
import preferences from '@ohos.data.preferences';

class SecureApiService {
  private httpRequest: http.HttpRequest;
  private preferences: preferences.Preferences;

  constructor(context: common.UIAbilityContext) {
    this.httpRequest = http.createHttp();
    this.preferences = preferences.getPreferencesSync(context, 'authData');
  }

  private async getAuthHeader(): Promise<any> {
    const token = await this.preferences.get('token', '');
    return {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    };
  }

  async getUserProfile(): Promise<any> {
    try {
      const headers = await this.getAuthHeader();
      
      const response = await this.httpRequest.request(
        'https://api.example.com/user/profile',
        {
          method: http.RequestMethod.GET,
          header: headers
        }
      );

      if (response.responseCode === 200) {
        return JSON.parse(response.result);
      } else if (response.responseCode === 401) {
        // token过期,需要重新登录
        throw new Error('认证失败,请重新登录');
      } else {
        throw new Error(`获取用户信息失败: ${response.responseCode}`);
      }
    } catch (error) {
      console.error('获取用户信息异常:', error);
      throw error;
    }
  }

  async updateUserProfile(data: any): Promise<any> {
    try {
      const headers = await this.getAuthHeader();
      
      const response = await this.httpRequest.request(
        'https://api.example.com/user/profile',
        {
          method: http.RequestMethod.PUT,
          header: headers,
          extraData: JSON.stringify(data)
        }
      );

      if (response.responseCode === 200) {
        return JSON.parse(response.result);
      } else if (response.responseCode === 401) {
        throw new Error('认证失败,请重新登录');
      } else {
        throw new Error(`更新用户信息失败: ${response.responseCode}`);
      }
    } catch (error) {
      console.error('更新用户信息异常:', error);
      throw error;
    }
  }
}

10. 错误处理与重试机制

错误处理示例:

import http from '@ohos.net.http';

class ApiClient {
  private httpRequest: http.HttpRequest;

  constructor() {
    this.httpRequest = http.createHttp();
  }

  async requestWithRetry(
    url: string,
    options: http.HttpRequestOptions,
    maxRetries: number = 3
  ): Promise<http.HttpResponse> {
    let lastError: Error;
    
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const response = await this.httpRequest.request(url, options);
        
        if (response.responseCode >= 200 && response.responseCode < 300) {
          return response;
        }
        
        // 如果是服务器错误,重试
        if (response.responseCode >= 500) {
          console.warn(`服务器错误,第${attempt}次重试: ${response.responseCode}`);
          await this.delay(1000 * attempt); // 指数退避
          continue;
        }
        
        // 客户端错误,不重试
        throw new Error(`请求失败: ${response.responseCode}`);
      } catch (error) {
        lastError = error;
        console.warn(`网络异常,第${attempt}次重试:`, error.message);
        
        if (attempt < maxRetries) {
          await this.delay(1000 * attempt);
        }
      }
    }
    
    throw lastError || new Error('请求失败');
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

11. 性能优化与最佳实践

1. 请求复用

import http from '@ohos.net.http';

// 单例模式管理HTTP请求
class HttpManager {
  private static instance: HttpManager;
  private httpRequest: http.HttpRequest;

  private constructor() {
    this.httpRequest = http.createHttp();
    this.httpRequest.setTimeout(10000);
  }

  static getInstance(): HttpManager {
    if (!HttpManager.instance) {
      HttpManager.instance = new HttpManager();
    }
    return HttpManager.instance;
  }

  async request(url: string, options: http.HttpRequestOptions): Promise<http.HttpResponse> {
    return await this.httpRequest.request(url, options);
  }
}

2. 请求取消

import http from '@ohos.net.http';

class CancelableRequest {
  private httpRequest: http.HttpRequest;
  private isCanceled: boolean = false;

  constructor() {
    this.httpRequest = http.createHttp();
  }

  async request(url: string, options: http.HttpRequestOptions): Promise<http.HttpResponse> {
    if (this.isCanceled) {
      throw new Error('请求已取消');
    }

    try {
      const response = await this.httpRequest.request(url, options);
      
      if (this.isCanceled) {
        throw new Error('请求已取消');
      }
      
      return response;
    } catch (error) {
      if (this.isCanceled) {
        throw new Error('请求已取消');
      }
      throw error;
    }
  }

  cancel() {
    this.isCanceled = true;
    // 注意:目前HTTP模块不支持直接取消请求,需要手动标记
  }
}

3. 请求拦截器

import http from '@ohos.net.http';
import preferences from '@ohos.data.preferences';

class InterceptorService {
  private httpRequest: http.HttpRequest;
  private preferences: preferences.Preferences;

  constructor(context: common.UIAbilityContext) {
    this.httpRequest = http.createHttp();
    this.preferences = preferences.getPreferencesSync(context, 'authData');
  }

  // 请求拦截器
  private async requestInterceptor(options: http.HttpRequestOptions): Promise<http.HttpRequestOptions> {
    // 添加认证token
    const token = await this.preferences.get('token', '');
    if (token) {
      options.header = options.header || {};
      options.header['Authorization'] = `Bearer ${token}`;
    }
    
    // 添加公共请求头
    options.header = {
      'Content-Type': 'application/json',
      'User-Agent': 'MyApp/1.0',
      ...options.header
    };
    
    return options;
  }

  // 响应拦截器
  private responseInterceptor(response: http.HttpResponse): http.HttpResponse {
    if (response.responseCode === 401) {
      // token过期处理
      this.handleTokenExpired();
      throw new Error('认证过期,请重新登录');
    }
    
    return response;
  }

  private handleTokenExpired() {
    // 清除token,跳转到登录页
    this.preferences.delete('token');
    // router.replaceUrl({ url: 'pages/LoginPage' });
  }

  async request(url: string, options: http.HttpRequestOptions): Promise<http.HttpResponse> {
    const processedOptions = await this.requestInterceptor(options);
    const response = await this.httpRequest.request(url, processedOptions);
    return this.responseInterceptor(response);
  }
}

三、总结:网络请求的核心要点

✅ 核心知识点回顾

  1. HTTP模块基础:掌握http.createHttp()创建请求对象,支持GET、POST、PUT、DELETE等方法
  2. 请求配置:设置请求头、请求体、超时时间等参数
  3. 数据格式:支持JSON、FormData等多种数据格式
  4. 错误处理:正确处理网络异常、服务器错误、认证失败等情况
  5. 性能优化:实现请求复用、请求取消、重试机制等优化策略

⚠️ 常见问题与解决方案

  1. 网络权限问题:确保在config.json中声明ohos.permission.INTERNET权限
  2. 跨域问题:服务器需要配置CORS,或者使用代理服务器
  3. HTTPS证书问题:确保服务器使用有效的SSL证书
  4. 请求超时:合理设置超时时间,实现重试机制
  5. 内存泄漏:及时释放HTTP请求对象,避免内存泄漏

🎯 最佳实践建议

  1. 封装请求服务:将HTTP请求封装到独立的服务类中,提高代码复用性
  2. 错误统一处理:实现统一的错误处理机制,避免重复代码
  3. 请求拦截器:使用拦截器统一处理认证、日志、错误等公共逻辑
  4. 性能监控:监控网络请求性能,优化慢请求
  5. 安全考虑:敏感数据使用HTTPS加密传输,token安全存储
posted @ 2025-12-23 23:04  蓝莓Reimay  阅读(1)  评论(0)    收藏  举报