课程地址:https://blog.csdn.net/yyz_1987/article/details/153418477

在移动应用开发中,网络请求是连接应用与后端服务的重要桥梁。鸿蒙应用也不例外,无论是获取数据、提交用户信息还是进行实时通信,都需要通过网络请求来实现。在"爱影家"APP中,我们需要通过网络请求获取电影列表、电影详情、视频播放地址等信息,这些数据是应用运行的核心。

本章将详细介绍鸿蒙应用中的网络请求相关知识,包括HTTP协议基础、鸿蒙官方网络组件的使用以及第三方库axios的集成,帮助开发者掌握鸿蒙应用中的数据交互技术。

二、HTTP协议基础

1. HTTP协议概述

HTTP(超文本传输协议)是客户端与服务器之间进行通信的一种协议。在鸿蒙应用开发中,我们主要通过HTTP协议与服务器进行数据交互。HTTP请求本质上是客户端向服务器发出请求的过程,而服务器则根据请求返回相应的响应。

2. HTTP请求的基本组成

一个完整的HTTP请求包含以下几个部分:

2.1 请求方法

常见的HTTP请求方法包括:

  • GET:请求获取服务器上的资源
  • POST:向服务器提交数据,可能会创建新资源或修改现有资源
  • PUT:上传文件或更新资源
  • DELETE:请求服务器删除指定资源
  • OPTIONS:获取目标资源支持的HTTP方法
  • HEAD:类似于GET,但只返回响应头,不返回响应体
  • PATCH:对资源进行部分修改

在"爱影家"APP中,我们主要使用GET和POST方法获取电影数据。

2.2 URL

URL(统一资源定位符)用于指定要访问的资源地址,也可以理解为向服务器请求资源的API接口。URL通常包含协议、域名、端口号、路径和查询参数等部分。

2.3 请求头

请求头包含了一些附加信息,如用户代理、内容类型、认证信息等。这些信息帮助服务器更好地理解和处理请求。

2.4 请求体

请求体主要用于承载客户端向服务器发送的数据,如表单数据、JSON数据等。在POST、PUT等请求中,请求体尤为重要。

3. HTTP响应的基本组成

HTTP响应主要包含以下部分:

3.1 状态码

状态码表示服务器对请求的处理结果,常见的状态码包括:

  • 200 OK:请求成功
  • 400 Bad Request:请求参数错误
  • 401 Unauthorized:未授权
  • 403 Forbidden:禁止访问
  • 404 Not Found:请求的资源不存在
  • 500 Internal Server Error:服务器内部错误

3.2 响应头

响应头包含了服务器返回的附加信息,如内容类型、缓存控制等。

3.3 响应体

响应体是服务器返回的核心数据,通常以JSON、XML等格式存在。在鸿蒙应用中,我们主要处理JSON格式的响应数据。

三、鸿蒙官方网络组件

1. Network Kit介绍

鸿蒙系统提供了Network Kit(网络服务),主要用于进行HTTP数据请求、WebSocket连接、Socket连接以及网络连接管理等。Network Kit对应的系统API模块为@ohos.net.http,现在已归类到@kit.NetworkKit包中。

2. 权限配置

在使用网络服务之前,需要在应用的配置文件中声明相应的权限:

// module.json5
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.GET_NETWORK_INFO"
      }
    ]
  }
}

这些权限分别用于允许应用访问互联网和获取设备网络信息。

3. 基本使用步骤

使用Network Kit发送HTTP请求的基本步骤如下:

3.1 导入模块

import { http } from '@kit.NetworkKit';

3.2 创建HTTP请求任务

let httpRequest = http.createHttp();

3.3 发起网络请求

httpRequest.request(
  'https://api.example.com/movies',
  {
    method: http.RequestMethod.GET,
    header: {
      'Content-Type': 'application/json'
    },
    expectDataType: http.HttpDataType.STRING
  },
  (err, data) => {
    if (!err) {
      // 处理成功响应
      console.log('Result:', data.result);
      console.log('Status Code:', data.responseCode);
      // 解析JSON数据
      const jsonData = JSON.parse(data.result as string);
      this.handleMovieList(jsonData);
    } else {
      // 处理错误
      console.error('Error:', err);
    }
    // 及时销毁请求任务
    httpRequest.destroy();
  }
);

3.4 使用Promise方式

除了回调方式外,Network Kit也支持Promise方式的异步操作:

async function fetchData() {
  let httpRequest = http.createHttp();
  try {
    const data = await httpRequest.request(
      'https://api.example.com/movies',
      {
        method: http.RequestMethod.GET
      }
    );
    console.log('Result:', data.result);
    return JSON.parse(data.result as string);
  } catch (err) {
    console.error('Error:', err);
    throw err;
  } finally {
    httpRequest.destroy();
  }
}

四、第三方库axios的使用

1. axios简介

axios是一个流行的HTTP客户端库,用于发送HTTP请求。在鸿蒙应用开发中,我们可以使用适配鸿蒙系统的axios三方库(如nutpi/axios),它提供了更简洁、更强大的API,使得网络请求的处理更加便捷。

2. axios的优势

相比于直接使用官方Network Kit,axios具有以下优势:

  • 更简洁的API:链式调用使得代码更易读、易写
  • 自动转换JSON数据:自动处理请求和响应的JSON转换
  • 拦截器支持:可以在请求发送前和响应接收后进行拦截处理
  • 请求取消:支持取消正在进行的请求
  • 错误处理:提供更完善的错误处理机制
  • 请求/响应转换:可以对请求和响应数据进行转换处理

3. axios的基本使用

3.1 安装与导入

首先需要在项目中添加axios依赖,然后导入使用:

import axios from '@nutpi/axios';

3.2 创建axios实例

创建一个配置了基础URL、超时等参数的axios实例:

const axiosClient = axios.create({
  baseURL: 'http://120.27.146.247:8000/api/v1',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
});

3.3 发送GET请求

async function getMovieList() {
  try {
    const response = await axiosClient.get('/movies');
    return response.data;
  } catch (error) {
    console.error('Failed to get movie list:', error);
    throw error;
  }
}

3.4 发送POST请求

在"爱影家"APP中,获取电影详情使用的是POST请求:

export const getDetailMv = (id: string): Promise<DetailMvResp> => {
  return axiosClient.post({
    url: '/detailmovie',
    data: { id: id }
  });
};

// 调用示例
async function fetchMovieDetail(movieId: string) {
  try {
    const detailData = await getDetailMv(movieId);
    this.detailData = detailData;
  } catch (error) {
    console.error('Failed to fetch movie detail:', error);
    // 显示错误提示
  }
}

4. 拦截器的使用

拦截器是axios的一个强大特性,它允许我们在请求发送前和响应接收后执行一些操作。

4.1 请求拦截器

axiosClient.interceptors.request.use(
  config => {
    // 在发送请求之前做些什么,如添加token
    const token = getToken();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  error => {
    // 处理请求错误
    return Promise.reject(error);
  }
);

4.2 响应拦截器

axiosClient.interceptors.response.use(
  response => {
    // 对响应数据做点什么
    return response.data;
  },
  error => {
    // 处理响应错误
    if (error.response) {
      // 服务器返回了错误状态码
      switch (error.response.status) {
        case 401:
          // 未授权,处理登录逻辑
          handleUnauthorized();
          break;
        case 404:
          // 资源不存在
          showToast('请求的资源不存在');
          break;
        case 500:
          // 服务器错误
          showToast('服务器错误,请稍后重试');
          break;
        default:
          showToast(`请求失败: ${error.response.status}`);
      }
    } else if (error.request) {
      // 请求已发出,但没有收到响应
      showToast('网络连接失败,请检查网络');
    } else {
      // 请求配置出错
      showToast('请求配置错误');
    }
    return Promise.reject(error);
  }
);

五、数据模型定义与管理

1. 定义数据模型

为了更好地管理从服务器获取的数据,我们需要定义清晰的数据模型。在TypeScript中,我们可以使用接口(Interface)来定义数据模型。

// 电影详情响应接口
export interface DetailMvResp {
  id: string;
  title: string;
  images: string;
  year: string;
  genre: string;
  wish_count: number;
  rating: number;
  summary: string;
  casts: DetailMvRespCast[];
  // 其他字段...
}

// 影人信息接口
export interface DetailMvRespCast {
  id: string;
  name: string;
  avatar: string;
  role: string;
}

// 电影视频源响应接口
export interface MvSourceResp {
  sources: string[];
  // 其他字段...
}

2. 数据状态管理

在鸿蒙应用中,我们可以使用@State装饰器来管理组件的状态数据。当状态数据发生变化时,UI会自动更新。

@Component
struct DetailPage {
  @State detailData: DetailMvResp | null = null;
  @State srcData: MvSourceResp | null = null;
  @State isLoading: boolean = false;
  
  async onPageShow() {
    this.isLoading = true;
    try {
      // 并行请求电影详情和视频源
      const [detailResult, srcResult] = await Promise.all([
        getDetailMv(this.movieId),
        getMovieSrc(this.movieId)
      ]);
      this.detailData = detailResult;
      this.srcData = srcResult;
    } catch (error) {
      console.error('Failed to load data:', error);
    } finally {
      this.isLoading = false;
    }
  }
  
  build() {
    // UI渲染
  }
}

六、网络请求的最佳实践

1. 错误处理

完善的错误处理是网络请求的重要部分,它可以提升用户体验,避免应用崩溃。

async function safeFetch<T>(fetchFn: () => Promise<T>): Promise<T | null> {
  try {
    const result = await fetchFn();
    return result;
  } catch (error) {
    console.error('Network error:', error);
    // 显示用户友好的错误提示
    promptAction.showToast({
      message: '网络请求失败,请稍后重试',
      duration: 2000
    });
    return null;
  }
}

// 使用示例
const movieList = await safeFetch(() => getMovieList());
if (movieList) {
  // 处理数据
} else {
  // 处理失败情况
}

2. 缓存策略

合理的缓存策略可以减少不必要的网络请求,提升应用性能和用户体验。

// 简单的内存缓存
class CacheManager {
  private cache: Map<string, { data: any; timestamp: number }> = new Map();
  private maxAge: number = 5 * 60 * 1000; // 5分钟
  
  set(key: string, data: any): void {
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
  }
  
  get(key: string): any | null {
    const cached = this.cache.get(key);
    if (!cached) return null;
    
    // 检查缓存是否过期
    if (Date.now() - cached.timestamp > this.maxAge) {
      this.cache.delete(key);
      return null;
    }
    
    return cached.data;
  }
  
  clear(): void {
    this.cache.clear();
  }
}

const cacheManager = new CacheManager();

// 在网络请求中使用缓存
async function getMovieListWithCache(): Promise<MovieList> {
  const cacheKey = 'movie_list';
  
  // 尝试从缓存获取
  const cachedData = cacheManager.get(cacheKey);
  if (cachedData) {
    return cachedData;
  }
  
  // 缓存未命中,发起网络请求
  const data = await getMovieList();
  
  // 更新缓存
  cacheManager.set(cacheKey, data);
  
  return data;
}

3. 请求节流与防抖

对于频繁触发的请求(如搜索功能),使用节流或防抖技术可以减少请求次数,提升性能。

// 防抖函数
function debounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: number | null = null;
  
  return function(...args: Parameters<T>) {
    if (timeout !== null) {
      clearTimeout(timeout);
    }
    
    timeout = setTimeout(() => {
      func.apply(this, args);
    }, wait);
  };
}

// 在搜索功能中使用防抖
@Component
struct SearchPage {
  @State searchText: string = '';
  
  // 创建防抖的搜索函数
  private debouncedSearch = debounce((text: string) => {
    this.performSearch(text);
  }, 500);
  
  onSearchChange(event: TextInputChangeEvent) {
    this.searchText = event.text;
    this.debouncedSearch(this.searchText);
  }
  
  async performSearch(keyword: string) {
    if (keyword.trim().length > 0) {
      try {
        const results = await searchMovies(keyword);
        // 处理搜索结果
      } catch (error) {
        console.error('Search failed:', error);
      }
    }
  }
}

4. 批量请求处理

对于需要同时发起多个请求的场景,可以使用Promise.all来并行处理,提高效率。

async function loadMovieDetails(movieIds: string[]) {
  try {
    // 创建所有请求的Promise数组
    const promises = movieIds.map(id => getDetailMv(id));
    
    // 并行执行所有请求
    const results = await Promise.all(promises);
    
    // 处理所有结果
    return results;
  } catch (error) {
    console.error('Failed to load movie details:', error);
    throw error;
  }
}

七、总结

网络请求是鸿蒙应用开发中不可或缺的一部分。通过本文的介绍,我们了解了HTTP协议基础、鸿蒙官方Network Kit的使用以及第三方库axios的集成,这些知识对于构建功能完整的鸿蒙应用至关重要。

在实际开发中,我们需要根据具体需求选择合适的网络请求方式,同时注重错误处理、性能优化和用户体验。通过合理使用拦截器、缓存策略、防抖节流等技术,我们可以构建出高效、稳定的网络交互模块。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐