鸿蒙开发之:网络请求与数据处理

33 阅读7分钟

本文字数:约3500字 | 预计阅读时间:15分钟

前置知识:建议先学习本系列前四篇,特别是状态管理与数据绑定

实战价值:掌握网络请求,让应用从本地走向云端,实现数据动态更新

系列导航:本文是《鸿蒙开发系列》第5篇,下篇将讲解本地数据存储方案

一、网络请求基础:从本地到云端 在移动应用中,网络请求是实现动态内容、用户交互和数据同步的核心功能。鸿蒙提供了强大的网络能力支持,让我们能够轻松地连接云端服务。

1.1 网络请求的三种方式 HTTP/HTTPS请求:最常用的RESTful API调用方式

WebSocket:实时双向通信,适合聊天、实时数据推送

Socket:底层网络通信,适合自定义协议

二、HTTP请求全解析 2.1 权限配置 在module.json5中添加网络权限:

json

复制

下载

{ "module": { "requestPermissions": [ { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.GET_NETWORK_INFO" } ] } } 2.2 基础GET请求 typescript

复制

下载

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

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

// GET请求示例 async function getRequest() { try { let response = await httpRequest.request( 'jsonplaceholder.typicode.com/posts/1', { method: http.RequestMethod.GET, connectTimeout: 60000, readTimeout: 60000, header: { 'Content-Type': 'application/json' } } );

console.log('Response Code:', response.responseCode);
console.log('Headers:', response.header);

// 解析响应数据
let result = JSON.parse(response.result.toString());
console.log('Response Data:', result);

return result;

} catch (error) { console.error('GET Request Error:', error); throw error; } } 2.3 POST请求与参数传递 typescript

复制

下载

async function postRequest() { try { let response = await httpRequest.request( 'jsonplaceholder.typicode.com/posts', { method: http.RequestMethod.POST, header: { 'Content-Type': 'application/json' }, extraData: JSON.stringify({ title: 'foo', body: 'bar', userId: 1 }), connectTimeout: 60000, readTimeout: 60000 } );

return JSON.parse(response.result.toString());

} catch (error) { console.error('POST Request Error:', error); throw error; } } 2.4 多类型请求示例 typescript

复制

下载

// PUT请求 - 更新资源 async function putRequest(id: number, data: any) { let response = await httpRequest.request( https://jsonplaceholder.typicode.com/posts/${id}, { method: http.RequestMethod.PUT, header: { 'Content-Type': 'application/json' }, extraData: JSON.stringify(data) } ); return JSON.parse(response.result.toString()); }

// DELETE请求 - 删除资源 async function deleteRequest(id: number) { let response = await httpRequest.request( https://jsonplaceholder.typicode.com/posts/${id}, { method: http.RequestMethod.DELETE } ); return response.responseCode === 200; }

// 带查询参数的请求 async function getWithParams(params: Record<string, string>) { let url = 'jsonplaceholder.typicode.com/posts'; let queryString = Object.keys(params) .map(key => ${encodeURIComponent(key)}=${encodeURIComponent(params[key])}) .join('&');

if (queryString) { url += ?${queryString}; }

let response = await httpRequest.request( url, { method: http.RequestMethod.GET } );

return JSON.parse(response.result.toString()); } 三、网络请求封装:创建通用HTTP客户端 为了提升代码复用性和可维护性,我们需要封装一个通用的HTTP客户端。

3.1 HTTP客户端类 typescript

复制

下载

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

// 定义响应类型 interface ApiResponse<T = any> { code: number; message: string; data: T; success: boolean; }

// 定义请求配置 interface RequestConfig { url: string; method: http.RequestMethod; headers?: Record<string, string>; params?: Record<string, any>; data?: any; timeout?: number; }

class HttpClient { private httpRequest: http.HttpRequest; private baseURL: string; private defaultHeaders: Record<string, string>;

constructor(baseURL: string = '') { this.httpRequest = http.createHttp(); this.baseURL = baseURL; this.defaultHeaders = { 'Content-Type': 'application/json', 'Accept': 'application/json' }; }

// 设置默认请求头 setDefaultHeaders(headers: Record<string, string>) { this.defaultHeaders = { ...this.defaultHeaders, ...headers }; }

// 处理URL和查询参数 private buildURL(url: string, params?: Record<string, any>): string { let fullURL = this.baseURL ? ${this.baseURL}${url} : url;

if (params) {
  const queryString = Object.keys(params)
    .filter(key => params[key] !== undefined && params[key] !== null)
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
    .join('&');
  
  if (queryString) {
    fullURL += `${fullURL.includes('?') ? '&' : '?'}${queryString}`;
  }
}

return fullURL;

}

// 发起请求 async request<T = any>(config: RequestConfig): Promise<ApiResponse> { try { // 构建完整URL const fullURL = this.buildURL(config.url, config.params);

  // 合并请求头
  const headers = {
    ...this.defaultHeaders,
    ...config.headers
  };
  
  // 发起请求
  const response = await this.httpRequest.request(
    fullURL,
    {
      method: config.method,
      header: headers,
      extraData: config.data ? JSON.stringify(config.data) : undefined,
      connectTimeout: config.timeout || 60000,
      readTimeout: config.timeout || 60000
    }
  );
  
  // 处理响应
  const responseData = response.result.toString();
  let data: any;
  
  try {
    data = responseData ? JSON.parse(responseData) : null;
  } catch {
    data = responseData;
  }
  
  // 判断请求是否成功(这里根据业务需求调整)
  const isSuccess = response.responseCode >= 200 && response.responseCode < 300;
  
  return {
    code: response.responseCode,
    message: isSuccess ? '请求成功' : '请求失败',
    data: data,
    success: isSuccess
  };
} catch (error) {
  console.error('HTTP Request Error:', error);
  
  return {
    code: -1,
    message: error.message || '网络请求失败',
    data: null as any,
    success: false
  };
}

}

// 快捷方法 async get<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>) { return this.request({ url, method: http.RequestMethod.GET, params, headers }); }

async post<T = any>(url: string, data?: any, headers?: Record<string, string>) { return this.request({ url, method: http.RequestMethod.POST, data, headers }); }

async put<T = any>(url: string, data?: any, headers?: Record<string, string>) { return this.request({ url, method: http.RequestMethod.PUT, data, headers }); }

async delete<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>) { return this.request({ url, method: http.RequestMethod.DELETE, params, headers }); } }

// 创建全局实例 export const httpClient = new HttpClient('jsonplaceholder.typicode.com'); 3.2 请求拦截与响应拦截 typescript

复制

下载

class EnhancedHttpClient extends HttpClient { private requestInterceptors: Array<(config: RequestConfig) => RequestConfig> = []; private responseInterceptors: Array<(response: ApiResponse) => ApiResponse> = [];

// 添加请求拦截器 addRequestInterceptor(interceptor: (config: RequestConfig) => RequestConfig) { this.requestInterceptors.push(interceptor); }

// 添加响应拦截器 addResponseInterceptor(interceptor: (response: ApiResponse) => ApiResponse) { this.responseInterceptors.push(interceptor); }

// 重写请求方法 async request<T = any>(config: RequestConfig): Promise<ApiResponse> { // 执行请求拦截器 let processedConfig = { ...config }; for (const interceptor of this.requestInterceptors) { processedConfig = interceptor(processedConfig); }

// 发起请求
const response = await super.request<T>(processedConfig);

// 执行响应拦截器
let processedResponse = { ...response };
for (const interceptor of this.responseInterceptors) {
  processedResponse = interceptor(processedResponse);
}

return processedResponse;

} }

// 使用示例 const enhancedClient = new EnhancedHttpClient();

// 添加请求拦截器(添加认证token) enhancedClient.addRequestInterceptor((config) => { const token = localStorage.get('token'); // 假设有localStorage if (token) { config.headers = { ...config.headers, 'Authorization': Bearer ${token} }; } return config; });

// 添加响应拦截器(统一错误处理) enhancedClient.addResponseInterceptor((response) => { if (response.code === 401) { // token过期,跳转到登录页 router.push('/login'); }

if (!response.success) { // 统一错误提示 showToast(response.message); }

return response; }); 四、数据处理与类型安全 4.1 定义数据类型 typescript

复制

下载

// 用户类型 interface User { id: number; name: string; email: string; phone: string; website: string; company: { name: string; catchPhrase: string; bs: string; }; }

// 文章类型 interface Post { id: number; userId: number; title: string; body: string; }

// 评论类型 interface Comment { id: number; postId: number; name: string; email: string; body: string; } 4.2 API服务层 typescript

复制

下载

// api/user.ts import { httpClient } from '../utils/HttpClient';

export class UserApi { // 获取用户列表 static async getUsers(): Promise<User[]> { const response = await httpClient.get<User[]>('/users'); return response.success ? response.data : []; }

// 获取单个用户 static async getUser(id: number): Promise<User | null> { const response = await httpClient.get(/users/${id}); return response.success ? response.data : null; }

// 创建用户 static async createUser(user: Omit<User, 'id'>): Promise<User | null> { const response = await httpClient.post('/users', user); return response.success ? response.data : null; }

// 更新用户 static async updateUser(id: number, user: Partial): Promise<User | null> { const response = await httpClient.put(/users/${id}, user); return response.success ? response.data : null; }

// 删除用户 static async deleteUser(id: number): Promise { const response = await httpClient.delete(/users/${id}); return response.success; } }

// api/post.ts export class PostApi { // 获取文章列表 static async getPosts(params?: { userId?: number; page?: number; limit?: number; }): Promise<Post[]> { const response = await httpClient.get<Post[]>('/posts', params); return response.success ? response.data : []; }

// 获取用户的所有文章 static async getUserPosts(userId: number): Promise<Post[]> { return this.getPosts({ userId }); }

// 获取文章的评论 static async getPostComments(postId: number): Promise<Comment[]> { const response = await httpClient.get<Comment[]>(/posts/${postId}/comments); return response.success ? response.data : []; } } 五、状态管理与网络请求结合 5.1 创建数据存储 typescript

复制

下载

// store/UserStore.ts import { UserApi } from '../api/UserApi'; import { PostApi } from '../api/PostApi'; import { User, Post, Comment } from '../models';

class UserStore { // 用户列表 private _users: User[] = []; private _currentUser: User | null = null; private _userPosts: Map<number, Post[]> = new Map(); private _loading: boolean = false; private _error: string | null = null;

// 获取用户列表 async loadUsers() { this._loading = true; this._error = null;

try {
  this._users = await UserApi.getUsers();
} catch (error) {
  this._error = error.message;
  console.error('加载用户失败:', error);
} finally {
  this._loading = false;
}

}

// 获取用户详情 async loadUser(id: number) { this._loading = true; this._error = null;

try {
  this._currentUser = await UserApi.getUser(id);
} catch (error) {
  this._error = error.message;
  console.error('加载用户详情失败:', error);
} finally {
  this._loading = false;
}

}

// 获取用户的文章 async loadUserPosts(userId: number) { this._loading = true;

try {
  const posts = await PostApi.getUserPosts(userId);
  this._userPosts.set(userId, posts);
} catch (error) {
  console.error('加载用户文章失败:', error);
} finally {
  this._loading = false;
}

}

// 获取用户文章(带缓存) async getUserPosts(userId: number): Promise<Post[]> { // 先检查缓存 if (this._userPosts.has(userId)) { return this._userPosts.get(userId)!; }

// 没有缓存则请求网络
await this.loadUserPosts(userId);
return this._userPosts.get(userId) || [];

}

// Getter方法 get users(): User[] { return this._users; }

get currentUser(): User | null { return this._currentUser; }

get loading(): boolean { return this._loading; }

get error(): string | null { return this._error; } }

export const userStore = new UserStore(); 5.2 在组件中使用 typescript

复制

下载

@Entry @Component struct UserListPage { @State users: User[] = []; @State loading: boolean = false; @State error: string | null = null;

aboutToAppear() { this.loadUsers(); }

async loadUsers() { this.loading = true; this.error = null;

try {
  this.users = await UserApi.getUsers();
} catch (error) {
  this.error = error.message;
  console.error('加载用户列表失败:', error);
} finally {
  this.loading = false;
}

}

build() { Column({ space: 20 }) { // 标题 Text('用户列表') .fontSize(24) .fontWeight(FontWeight.Bold)

  // 加载状态
  if (this.loading) {
    LoadingProgress()
      .width(50)
      .height(50)
    
    Text('加载中...')
      .fontSize(14)
      .fontColor('#999999')
  }
  
  // 错误提示
  if (this.error) {
    Column({ space: 10 }) {
      Image($r('app.media.error'))
        .width(60)
        .height(60)
      
      Text(this.error)
        .fontSize(14)
        .fontColor('#FF3B30')
      
      Button('重试')
        .width(120)
        .height(40)
        .onClick(() => {
          this.loadUsers();
        })
    }
    .padding(40)
  }
  
  // 用户列表
  if (!this.loading && !this.error) {
    List({ space: 12 }) {
      ForEach(this.users, (user: User) => {
        ListItem() {
          UserItem({ user: user })
        }
      })
    }
    .layoutWeight(1)
  }
  
  // 刷新按钮
  Button('刷新数据')
    .width(200)
    .height(50)
    .margin({ top: 20 })
    .onClick(() => {
      this.loadUsers();
    })
}
.width('100%')
.height('100%')
.padding(20)

} }

@Component struct UserItem { @Prop user: User;

build() { Row({ space: 15 }) { // 头像(使用首字母) Column() { Text(this.user.name.charAt(0).toUpperCase()) .fontSize(20) .fontColor(Color.White) } .width(50) .height(50) .backgroundColor('#007DFF') .borderRadius(25) .justifyContent(FlexAlign.Center)

  // 用户信息
  Column({ space: 5 }) {
    Text(this.user.name)
      .fontSize(16)
      .fontWeight(FontWeight.Bold)
    
    Text(this.user.email)
      .fontSize(14)
      .fontColor('#666666')
    
    Text(this.user.company.name)
      .fontSize(12)
      .fontColor('#999999')
  }
  .alignItems(HorizontalAlign.Start)
  .layoutWeight(1)
}
.width('100%')
.padding(15)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 4, color: '#10000000' })

} } 六、网络状态检测 6.1 检测网络状态 typescript

复制

下载

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

class NetworkManager { private netHandle: network.NetHandle;

constructor() { this.netHandle = network.getDefaultNet(); }

// 检查网络是否可用 async isNetworkAvailable(): Promise { try { const netCapabilities = await this.netHandle.getNetCapabilities(); return netCapabilities.hasCapability(network.NetCap.NET_CAPABILITY_INTERNET); } catch (error) { console.error('检查网络状态失败:', error); return false; } }

// 获取网络类型 async getNetworkType(): Promise { try { const netCapabilities = await this.netHandle.getNetCapabilities();

  if (netCapabilities.hasCapability(network.NetCap.NET_CAPABILITY_WIFI)) {
    return 'WiFi';
  } else if (netCapabilities.hasCapability(network.NetCap.NET_CAPABILITY_CELLULAR)) {
    return '移动网络';
  } else if (netCapabilities.hasCapability(network.NetCap.NET_CAPABILITY_ETHERNET)) {
    return '有线网络';
  } else {
    return '未知网络';
  }
} catch (error) {
  return '未知';
}

}

// 监听网络状态变化 subscribeToNetworkChanges(callback: (isConnected: boolean) => void) { network.on('netAvailable', () => { callback(true); });

network.on('netCapabilitiesChange', () => {
  this.isNetworkAvailable().then(callback);
});

network.on('netUnavailable', () => {
  callback(false);
});

} }

export const networkManager = new NetworkManager(); 6.2 在应用中使用网络检测 typescript

复制

下载

@Entry @Component struct NetworkAwareApp { @State isOnline: boolean = true; @State networkType: string = '未知';

aboutToAppear() { // 检查初始网络状态 this.checkNetworkStatus();

// 监听网络状态变化
networkManager.subscribeToNetworkChanges((connected) => {
  this.isOnline = connected;
  this.checkNetworkStatus();
});

}

async checkNetworkStatus() { this.isOnline = await networkManager.isNetworkAvailable(); this.networkType = await networkManager.getNetworkType(); }

build() { Column({ space: 20 }) { // 网络状态指示器 Row({ space: 10 }) { Image(this.isOnline ? r(app.media.wifi):r('app.media.wifi') : r('app.media.offline')) .width(24) .height(24)

    Text(this.isOnline ? `在线 (${this.networkType})` : '离线')
      .fontSize(14)
      .fontColor(this.isOnline ? '#34C759' : '#FF3B30')
  }
  .padding(10)
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
  
  // 主要内容
  if (this.isOnline) {
    UserListPage()
  } else {
    Column({ space: 20 }) {
      Image($r('app.media.offline'))
        .width(120)
        .height(120)
      
      Text('网络连接已断开')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
      
      Text('请检查您的网络连接后重试')
        .fontSize(14)
        .fontColor('#666666')
      
      Button('重试连接')
        .width(200)
        .height(50)
        .onClick(() => {
          this.checkNetworkStatus();
        })
    }
    .padding(40)
  }
}
.width('100%')
.height('100%')

} } 七、性能优化与缓存策略 7.1 内存缓存 typescript

复制

下载

class MemoryCache { private cache: Map<string, { data: any, timestamp: number }> = new Map(); private defaultTTL: number = 5 * 60 * 1000; // 5分钟

// 设置缓存 set(key: string, data: any, ttl?: number) { this.cache.set(key, { data, timestamp: Date.now() + (ttl || this.defaultTTL) }); }

// 获取缓存 get<T = any>(key: string): T | null { const item = this.cache.get(key);

if (!item) return null;

// 检查是否过期
if (Date.now() > item.timestamp) {
  this.cache.delete(key);
  return null;
}

return item.data;

}

// 删除缓存 delete(key: string) { this.cache.delete(key); }

// 清空缓存 clear() { this.cache.clear(); } }

// 使用缓存的HTTP客户端 class CachedHttpClient extends HttpClient { private cache = new MemoryCache();

async getWithCache<T = any>( url: string, params?: Record<string, any>, ttl?: number ): Promise<T | null> { // 生成缓存key const cacheKey = ${url}:${JSON.stringify(params || {})};

// 尝试从缓存获取
const cachedData = this.cache.get<T>(cacheKey);
if (cachedData) {
  console.log('从缓存获取数据:', url);
  return cachedData;
}

// 缓存未命中,发起网络请求
const response = await this.get<T>(url, params);

if (response.success && response.data) {
  // 缓存数据
  this.cache.set(cacheKey, response.data, ttl);
}

return response.data;

} } 7.2 图片加载优化 typescript

复制

下载

@Component struct OptimizedImage { @Prop src: string; @State isLoading: boolean = true; @State hasError: boolean = false; @State loadedImage: PixelMap | null = null;

// 图片缓存 private static imageCache: Map<string, PixelMap> = new Map();

aboutToAppear() { this.loadImage(); }

async loadImage() { // 检查缓存 if (OptimizedImage.imageCache.has(this.src)) { this.loadedImage = OptimizedImage.imageCache.get(this.src)!; this.isLoading = false; return; }

this.isLoading = true;
this.hasError = false;

try {
  // 创建HTTP请求
  const httpRequest = http.createHttp();
  const response = await httpRequest.request(
    this.src,
    {
      method: http.RequestMethod.GET,
      expectDataType: http.HttpDataType.IMAGE
    }
  );
  
  // 获取图片数据
  this.loadedImage = response.result as PixelMap;
  
  // 缓存图片
  OptimizedImage.imageCache.set(this.src, this.loadedImage);
  
} catch (error) {
  this.hasError = true;
  console.error('图片加载失败:', error);
} finally {
  this.isLoading = false;
}

}

build() { Stack() { // 加载中显示占位符 if (this.isLoading) { Column() .width('100%') .height('100%') .backgroundColor('#F5F5F5') }

  // 显示图片
  if (this.loadedImage && !this.isLoading) {
    Image(this.loadedImage)
      .width('100%')
      .height('100%')
      .objectFit(ImageFit.Cover)
  }
  
  // 加载失败显示错误图标
  if (this.hasError) {
    Image($r('app.media.error'))
      .width(40)
      .height(40)
  }
}

} } 八、错误处理与重试机制 8.1 智能重试机制 typescript

复制

下载

class RetryableHttpClient extends HttpClient { private maxRetries: number = 3; private retryDelay: number = 1000; // 1秒

setRetryConfig(maxRetries: number, retryDelay: number) { this.maxRetries = maxRetries; this.retryDelay = retryDelay; }

async requestWithRetry<T = any>(config: RequestConfig): Promise<ApiResponse> { let lastError: Error | null = null;

for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
  try {
    const response = await super.request<T>(config);
    
    // 如果请求成功,直接返回
    if (response.success) {
      return response;
    }
    
    // 如果是服务器错误,可能需要重试
    if (response.code >= 500) {
      console.warn(`服务器错误,第${attempt}次重试...`);
      lastError = new Error(response.message);
    } else {
      // 客户端错误,不需要重试
      return response;
    }
  } catch (error) {
    console.warn(`请求失败,第${attempt}次重试...`, error);
    lastError = error;
  }
  
  // 如果不是最后一次尝试,等待一段时间后重试
  if (attempt < this.maxRetries) {
    await this.delay(this.retryDelay * attempt); // 指数退避
  }
}

// 所有重试都失败了
return {
  code: -1,
  message: lastError?.message || '请求失败',
  data: null as any,
  success: false
};

}

private delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } } 九、实战:新闻客户端实现 typescript

复制

下载

@Entry @Component struct NewsApp { @State articles: any[] = []; @State loading: boolean = false; @State currentCategory: string = 'technology';

private httpClient = new HttpClient('newsapi.org/v2');

aboutToAppear() { this.loadNews(); }

async loadNews() { this.loading = true;

try {
  const response = await this.httpClient.get('/everything', {
    q: this.currentCategory,
    apiKey: 'YOUR_API_KEY', // 实际使用时替换为你的API Key
    pageSize: 20,
    language: 'zh'
  });
  
  if (response.success) {
    this.articles = response.data.articles || [];
  }
} catch (error) {
  console.error('加载新闻失败:', error);
} finally {
  this.loading = false;
}

}

build() { Column() { // 分类标签 Scroll(.horizontal) { Row({ space: 15 }) { ForEach(['technology', 'business', 'sports', 'health', 'science'], (category: string) => { Text(this.getCategoryName(category)) .fontSize(16) .fontColor(this.currentCategory === category ? '#007DFF' : '#333333') .fontWeight(this.currentCategory === category ? FontWeight.Bold : FontWeight.Normal) .padding({ left: 15, right: 15, top: 8, bottom: 8 }) .backgroundColor(this.currentCategory === category ? '#E6F2FF' : '#F5F5F5') .borderRadius(20) .onClick(() => { this.currentCategory = category; this.loadNews(); }) } ) } .padding({ left: 16, right: 16, top: 12, bottom: 12 }) } .scrollBar(BarState.Off)

  // 新闻列表
  if (this.loading) {
    LoadingProgress()
      .width(50)
      .height(50)
      .margin({ top: 100 })
  } else {
    List({ space: 12 }) {
      ForEach(this.articles, (article: any, index: number) => {
        ListItem() {
          NewsItem({ article: article })
        }
      })
    }
    .layoutWeight(1)
  }
}
.width('100%')
.height('100%')

}

private getCategoryName(category: string): string { const names: Record<string, string> = { 'technology': '科技', 'business': '商业', 'sports': '体育', 'health': '健康', 'science': '科学' }; return names[category] || category; } }

@Component struct NewsItem { @Prop article: any;

build() { Column({ space: 10 }) { // 图片 if (this.article.urlToImage) { OptimizedImage({ src: this.article.urlToImage }) .width('100%') .height(200) .objectFit(ImageFit.Cover) .borderRadius(12) }

  // 标题
  Text(this.article.title)
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
    .maxLines(2)
    .textOverflow({ overflow: TextOverflow.Ellipsis })
  
  // 描述
  Text(this.article.description)
    .fontSize(14)
    .fontColor('#666666')
    .maxLines(3)
    .textOverflow({ overflow: TextOverflow.Ellipsis })
  
  // 底部信息
  Row() {
    Text(this.article.source?.name || '未知来源')
      .fontSize(12)
      .fontColor('#999999')
    
    Blank()
    
    Text(this.formatDate(this.article.publishedAt))
      .fontSize(12)
      .fontColor('#999999')
  }
  .width('100%')
}
.padding(16)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 4, color: '#10000000' })

}

private formatDate(dateString: string): string { try { const date = new Date(dateString); return date.toLocaleDateString('zh-CN'); } catch { return dateString; } } } 十、总结与下期预告 10.1 本文要点回顾 HTTP请求基础:GET、POST、PUT、DELETE请求的使用

HTTP客户端封装:创建可复用的HTTP工具类

数据处理:类型安全的数据解析和API服务层

状态管理结合:网络请求与UI状态同步

网络检测:实时监控网络状态变化

性能优化:缓存策略和图片加载优化

错误处理:智能重试机制和统一错误处理

实战案例:完整的新闻客户端实现

10.2 下期预告:《鸿蒙开发之:本地数据存储方案》 下篇文章将深入讲解:

Preferences轻量级存储

关系型数据库的使用

文件操作与序列化

数据加密与安全存储

实战:实现应用的离线模式

动手挑战 任务1:天气预报应用 要求:

使用和风天气API或其他天气API

显示当前天气、温度、湿度、风速等信息

支持城市搜索和切换

实现天气数据缓存,减少网络请求

任务2:图片浏览应用 要求:

使用Unsplash API获取高质量图片

实现图片懒加载和无限滚动

支持图片收藏功能(本地存储)

实现图片分享功能

任务3:API封装优化 要求:

实现请求取消功能(防止重复请求)

实现请求优先级控制

实现离线队列(离线时保存请求,网络恢复后自动发送)

实现请求统计和性能监控

将你的代码分享到评论区,我会挑选优秀实现进行详细点评!

常见问题解答 Q:如何处理HTTPS证书问题? A:鸿蒙默认信任合法CA颁发的证书。如果是自签名证书,需要在config.json中配置网络安全配置。

Q:如何上传文件? A:使用FormData形式上传:

typescript

复制

下载

const formData = new FormData(); formData.append('file', fileObject); formData.append('name', 'filename');

await httpClient.post('/upload', formData, { 'Content-Type': 'multipart/form-data' }); Q:如何处理大文件下载? A:使用分片下载和断点续传,可以通过设置range头部实现。

Q:如何防止API密钥泄露? A:不要在客户端代码中硬编码API密钥,应该通过后端服务中转请求,或者使用动态令牌。

PS:现在HarmonyOS应用开发者认证正在做活动,初级和高级都可以免费学习及考试,赶快加入班级学习啦:【注意,考试只能从此唯一链接进入】 developer.huawei.com/consumer/cn…