前言
在鸿蒙开发的广袤天地中,网络层的搭建与封装无疑是构建高效、稳定应用的基石。继上篇的探索之后,本文将继续深入网络层的优化之旅,揭秘如何通过类型转换器、请求查询附加器以及丰富的常量参数,将网络层的构建艺术推向一个新的高度。
一、网络请求的深度优化
数据类型转换器:定义与实践
在网络请求的世界里,数据格式的转换至关重要。我们通过定义DataConverter
接口,实现了对请求与响应数据类型的灵活转换。
export interface DataConverter {requestConvert(extraData: string | Object | ArrayBuffer): string | Object | ArrayBuffer;responseConvert(data: string | Object | ArrayBuffer, responseType: http.HttpDataType): string | Object | ArrayBuffer;
}
默认数据转换器:JSON转换器的实现
我们实现了一个默认的JsonDataConverter
,它将请求数据转换为JSON字符串,并根据响应类型将响应数据转换为适当的格式。
export class JsonDataConverter implements DataConverter {requestConvert(extraData: string | Object | ArrayBuffer): string | Object | ArrayBuffer {// 将请求数据转换为JSON字符串return JSONUtil.beanToJsonStr(extraData);}responseConvert(data: string | Object | ArrayBuffer, responseType: http.HttpDataType): string | Object | ArrayBuffer {// 根据responseType将响应数据转换为相应的格式switch (responseType) {case http.HttpDataType.STRING:return JSON.parse(data as string);case http.HttpDataType.OBJECT:return data;default:return data;}}
}
参数附加器:灵活重组请求数据
参数附加器QueryParamAppender
接口允许我们对发送的请求数据进行重组,满足诸如参数签名等业务需求。
// 定义一个用于附加查询参数的接口
export interface QueryParamAppender {append(queryParams?: Map<string, number|string|boolean|Array<number> | Array<string> | Array<boolean> >): string|undefined;
}
默认附加器:简化查询参数的处理
通过CustomQueryParamAppender
的实现,我们简化了查询参数的编码和附加过程。
export class CustomQueryParamAppender implements QueryParamAppender {append(queryParams?: Map<string, string | number | boolean | number[] | string[] | boolean[]> | undefined): string|undefined {if (queryParams===undefined || queryParams.size === 0) {return;}const paramsArray: string[] = [];for (const qp of queryParams) {let key = qp[0]let value = qp[1]let encodedValue = '';if (Array.isArray(value)) {for (let i = 0; i < value.length; i++) {encodedValue += `${encodeURIComponent(`${key}[${i}]`)}=${encodeURIComponent(value[i].toString())}&`;}if (encodedValue.length > 0) {encodedValue = encodedValue.slice(0, -1); // 移除最后一个 '&'}} else {encodedValue = encodeURIComponent(key) + '=' + encodeURIComponent(value.toString());}paramsArray.push(encodedValue);}return paramsArray.join('&');}}
二、常量定义:构建网络层的坚实基础
通过定义一系列的常量,我们为网络请求的错误处理提供了统一的接口。这些常量不仅包括了各种网络错误的场景,还涵盖了HTTP状态码的含义,为开发者提供了清晰的指导。
{"name": "network_unavailable","value": "网络不可用"},{"name": "invalid_url_format","value": "URL格式不合法"},{"name": "invalid_url_not_exist","value": "URL不存在"},{"name": "parameter_error","value": "参数错误"},{"name": "permission_denied","value": "权限被拒绝"},{"name": "unsupported_protocol","value": "不支持的协议"},{"name": "bad_url_format","value": "URL使用错误的/非法的格式或缺少URL"},{"name": "could_not_resolve_proxy_name","value": "无法解析代理名称"},{"name": "could_not_resolve_host_name","value": "无法解析主机名"},{"name": "could_not_connect_to_server","value": "无法连接到服务器"},{"name": "weird_server_reply","value": "服务器回复异常"},{"name": "access_denied_to_remote_resource","value": "访问远程资源被拒绝"},{"name": "http2_framing_layer_error","value": "HTTP2帧层错误"},{"name": "transferred_partial_file","value": "传输了部分文件"},{"name": "failed_writing_data_to_disk","value": "将数据写入磁盘/应用程序失败"},{"name": "upload_failed","value": "上传失败"},{"name": "failed_to_open_read_local_data","value": "无法打开/读取本地数据"},{"name": "out_of_memory","value": "内存不足"},{"name": "timeout_reached","value": "达到超时时间"},{"name": "redirects_exceeded","value": "达到重定向的最大次数"},{"name": "server_returned_nothing","value": "服务器未返回任何内容(无头信息,无数据)"},{"name": "failed_sending_data_to_peer","value": "向对等端发送数据失败"},{"name": "failure_receiving_data_from_peer","value": "从对等端接收数据失败"},{"name": "ssl_certificate_problem","value": "本地SSL证书问题"},{"name": "unsupported_ssl_cipher","value": "不支持指定的SSL加密算法"},{"name": "ssl_peer_certificate_or_ssh_remote_key_not_ok","value": "SSL对等证书或SSH远程密钥不正确"},{"name": "unrecognized_http_content_or_transfer_encoding","value": "无法识别的HTTP内容或传输编码"},{"name": "maximum_file_size_exceeded","value": "超过最大文件大小"},{"name": "disk_full_or_allocation_exceeded","value": "磁盘已满或分配超过限制"},{"name": "remote_file_already_exists","value": "远程文件已存在"},{"name": "ssl_ca_cert_problem","value": "SSL CA证书问题(路径?访问权限?)"},{"name": "remote_file_not_found","value": "远程文件未找到"},{"name": "authentication_function_error","value": "身份验证函数返回错误"},{"name": "unknown_other_error","value": "未知的其他错误"},{"name": "bad_request","value": "客户端请求的语法错误,服务器无法理解。"},{"name": "unauthorized","value": "请求要求身份验证。"},{"name": "forbidden","value": "服务器理解请求客户端的请求,但是拒绝执行此请求。"},{"name": "not_found","value": "服务器无法根据客户端的请求找到资源(网页)。"},{"name": "method_not_allowed","value": "客户端请求中的方法被禁止。"},{"name": "request_timeout","value": "请求超时。"},{"name": "unsupported_media_type","value": "服务器不支持请求的格式(如请求中包含了服务器不支持的MIME类型)。"},{"name": "internal_server_error","value": "服务器内部错误,无法完成请求。"},{"name": "bad_gateway","value": "作为网关或代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。"},{"name": "service_unavailable","value": "由于超载或系统维护,服务器目前无法处理请求。"},{"name": "gateway_timeout","value": "作为网关或代理工作的服务器尝试执行请求时,未能及时从上游服务器收到需要的响应。"}
常量类代码使用
import { Application } from '../../app/Application'
import { NetworkError } from '../../exception/NetworkError'export class NetworkServiceErrorConst {// 网络不可用static readonly UN_AVILABLE: number = 100000// url错误static readonly URL_ERROR: number = 100001// url 不存在 错误static readonly URL_NOT_EXIST_ERROR: number = 100002static readonly PARAMETER_ERROR: number = 401;static readonly PERMISSION_DENIED: number = 201;static readonly UNSUPPORTED_PROTOCOL: number = 2300001;static readonly BAD_URL_FORMAT: number = 2300003;static readonly COULD_NOT_RESOLVE_PROXY_NAME: number = 2300005;static readonly COULD_NOT_RESOLVE_HOST_NAME: number = 2300006;static readonly COULD_NOT_CONNECT_TO_SERVER: number = 2300007;static readonly WEIRD_SERVER_REPLY: number = 2300008;static readonly ACCESS_DENIED_TO_REMOTE_RESOURCE: number = 2300009;static readonly HTTP2_FRAMING_LAYER_ERROR: number = 2300016;static readonly TRANSFERRED_PARTIAL_FILE: number = 2300018;static readonly FAILED_WRITING_DATA_TO_DISK: number = 2300023;static readonly UPLOAD_FAILED: number = 2300025;static readonly FAILED_TO_OPEN_READ_LOCAL_DATA: number = 2300026;static readonly OUT_OF_MEMORY: number = 2300027;static readonly TIMEOUT_REACHED: number = 2300028;static readonly REDIRECTS_EXCEEDED: number = 2300047;static readonly SERVER_RETURNED_NOTHING: number = 2300052;static readonly FAILED_SENDING_DATA_TO_PEER: number = 2300055;static readonly FAILURE_RECEIVING_DATA_FROM_PEER: number = 2300056;static readonly SSL_CERTIFICATE_PROBLEM: number = 2300058;static readonly UNSUPPORTED_SSL_CIPHER: number = 2300059;static readonly SSL_PEER_CERTIFICATE_OR_SSH_REMOTE_KEY_NOT_OK: number = 2300060;static readonly UNRECOGNIZED_HTTP_CONTENT_OR_TRANSFER_ENCODING: number = 2300061;static readonly MAXIMUM_FILE_SIZE_EXCEEDED: number = 2300063;static readonly DISK_FULL_OR_ALLOCATION_EXCEEDED: number = 2300070;static readonly REMOTE_FILE_ALREADY_EXISTS: number = 2300073;static readonly SSL_CA_CERT_PROBLEM: number = 2300077;static readonly REMOTE_FILE_NOT_FOUND: number = 2300078;static readonly AUTHENTICATION_FUNCTION_ERROR: number = 2300094;static readonly UNKNOWN_OTHER_ERROR: number = 2300999;// 4xx Client Errorstatic readonly BAD_REQUEST: number = 400;static readonly UNAUTHORIZED: number = 401;static readonly FORBIDDEN: number = 403;static readonly NOT_FOUND: number = 404;static readonly METHOD_NOT_ALLOWED: number = 405;static readonly REQUEST_TIMEOUT: number = 408;static readonly UNSUPPORTED_MEDIA_TYPE: number = 415;// 5xx Server Errorstatic readonly INTERNAL_SERVER_ERROR: number = 500;static readonly BAD_GATEWAY: number = 502;static readonly SERVICE_UNAVAILABLE: number = 503;static readonly GATEWAY_TIMEOUT: number = 504;public static getNetworkError(code: number): NetworkError{return new NetworkError(code, NetworkServiceErrorConst.getErrorReason(code));}public static getErrorReason(errorCode: number): string {let reason = "";switch (errorCode) {case NetworkServiceErrorConst.UN_AVILABLE:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.network_unavailable'));break;case NetworkServiceErrorConst.URL_ERROR:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.invalid_url_format'));break;case NetworkServiceErrorConst.URL_NOT_EXIST_ERROR:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.invalid_url_not_exist'));break;case NetworkServiceErrorConst.PARAMETER_ERROR:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.parameter_error'));break;case NetworkServiceErrorConst.PERMISSION_DENIED:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.permission_denied'));break;case NetworkServiceErrorConst.UNSUPPORTED_PROTOCOL:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.unsupported_protocol'));break;case NetworkServiceErrorConst.BAD_URL_FORMAT:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.bad_url_format'));break;case NetworkServiceErrorConst.COULD_NOT_RESOLVE_PROXY_NAME:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.could_not_resolve_proxy_name'));break;case NetworkServiceErrorConst.COULD_NOT_RESOLVE_HOST_NAME:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.could_not_resolve_host_name'));break;case NetworkServiceErrorConst.COULD_NOT_CONNECT_TO_SERVER:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.could_not_connect_to_server'));break;case NetworkServiceErrorConst.WEIRD_SERVER_REPLY:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.weird_server_reply'));break;case NetworkServiceErrorConst.ACCESS_DENIED_TO_REMOTE_RESOURCE:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.access_denied_to_remote_resource'));break;case NetworkServiceErrorConst.HTTP2_FRAMING_LAYER_ERROR:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.http2_framing_layer_error'));break;case NetworkServiceErrorConst.TRANSFERRED_PARTIAL_FILE:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.transferred_partial_file'));break;case NetworkServiceErrorConst.FAILED_WRITING_DATA_TO_DISK:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.failed_writing_data_to_disk'));break;case NetworkServiceErrorConst.UPLOAD_FAILED:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.upload_failed'));break;case NetworkServiceErrorConst.FAILED_TO_OPEN_READ_LOCAL_DATA:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.failed_to_open_read_local_data'));break;case NetworkServiceErrorConst.OUT_OF_MEMORY:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.out_of_memory'));break;case NetworkServiceErrorConst.TIMEOUT_REACHED:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.timeout_reached'));break;case NetworkServiceErrorConst.REDIRECTS_EXCEEDED:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.redirects_exceeded'));break;case NetworkServiceErrorConst.SERVER_RETURNED_NOTHING:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.server_returned_nothing'));break;case NetworkServiceErrorConst.FAILED_SENDING_DATA_TO_PEER:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.failed_sending_data_to_peer'));break;case NetworkServiceErrorConst.FAILURE_RECEIVING_DATA_FROM_PEER:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.failure_receiving_data_from_peer'));break;case NetworkServiceErrorConst.SSL_CERTIFICATE_PROBLEM:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.ssl_certificate_problem'));break;case NetworkServiceErrorConst.UNSUPPORTED_SSL_CIPHER:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.unsupported_ssl_cipher'));break;case NetworkServiceErrorConst.SSL_PEER_CERTIFICATE_OR_SSH_REMOTE_KEY_NOT_OK:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.ssl_peer_certificate_or_ssh_remote_key_not_ok'));break;case NetworkServiceErrorConst.UNRECOGNIZED_HTTP_CONTENT_OR_TRANSFER_ENCODING:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.unrecognized_http_content_or_transfer_encoding'));break;case NetworkServiceErrorConst.MAXIMUM_FILE_SIZE_EXCEEDED:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.maximum_file_size_exceeded'));break;case NetworkServiceErrorConst.DISK_FULL_OR_ALLOCATION_EXCEEDED:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.disk_full_or_allocation_exceeded'));break;case NetworkServiceErrorConst.REMOTE_FILE_ALREADY_EXISTS:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.remote_file_already_exists'));break;case NetworkServiceErrorConst.SSL_CA_CERT_PROBLEM:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.ssl_ca_cert_problem'));break;case NetworkServiceErrorConst.REMOTE_FILE_NOT_FOUND:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.remote_file_not_found'));break;case NetworkServiceErrorConst.AUTHENTICATION_FUNCTION_ERROR:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.authentication_function_error'));break;case NetworkServiceErrorConst.UNKNOWN_OTHER_ERROR:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.unknown_other_error'));break;case NetworkServiceErrorConst.BAD_REQUEST:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.bad_request'));break;case NetworkServiceErrorConst.UNAUTHORIZED:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.unauthorized'));break;case NetworkServiceErrorConst.FORBIDDEN:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.forbidden'));break;case NetworkServiceErrorConst.NOT_FOUND:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.not_found'));break;case NetworkServiceErrorConst.METHOD_NOT_ALLOWED:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.method_not_allowed'));break;case NetworkServiceErrorConst.REQUEST_TIMEOUT:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.request_timeout'));break;case NetworkServiceErrorConst.UNSUPPORTED_MEDIA_TYPE:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.unsupported_media_type'));break;case NetworkServiceErrorConst.INTERNAL_SERVER_ERROR:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.internal_server_error'));break;case NetworkServiceErrorConst.BAD_GATEWAY:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.bad_gateway'));break;case NetworkServiceErrorConst.SERVICE_UNAVAILABLE:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.service_unavailable'));break;case NetworkServiceErrorConst.GATEWAY_TIMEOUT:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.gateway_timeout'));break;default:reason = Application.getInstance().resourceManager.getStringSync($r('app.string.unknown_other_error'));break;}return reason;}}
三、异常定义:清晰的错误处理策略
我们重新封装了网络请求错误,定义了BaseError
和NetworkError
等类,使得错误类型一目了然,便于开发者快速定位问题。
// 自定义错误类型
import { http } from '@kit.NetworkKit';export abstract class BaseError extends Error{}//基本网络错误
export class NetworkError extends BaseError {code : numberconstructor(code: number,message: string) {super(message);this.name = 'NetworkError'this.code = code}
}//网络请求code错误
export class NetworkResponseError extends BaseError {code : http.ResponseCode | number;constructor(code: http.ResponseCode | number,message: string) {super(message);this.name = 'NetworkResponseError'this.code = code}
}
四、拦截器:网络请求的守卫
通过优化拦截器接口,我们能够在请求发送前后以及发生错误时,执行特定的逻辑,如日志记录、权限验证等。
export interface NetworkInterceptor {beforeRequest(request: RequestOptions, httprequest: http.HttpRequest): Promise<void> | void;afterResponse(response: http.HttpResponse , request: RequestOptions, httprequest: http.HttpRequest): Promise<void> | void;onError(error: BaseError, request: RequestOptions, httprequest: http.HttpRequest): Promise<void> | void;
}
拦截器默认实现:
import { NetworkInterceptor } from './NetworkInterceptor';
import { NetworkServiceErrorConst } from '../NetworkServiceErrorConst';
import { RequestOptions } from '../NetworkService';
import http from '@ohos.net.http';
import { LibLogManager } from '../../LibLog';
import { BaseError } from '../../../exception/NetworkError';
import { JSONUtil } from '../../JSONUtil';const TAG = "DefaultInterceptor"// 创建一个符合RequestOptions接口的对象
const requestOptions: RequestOptions = {baseUrl: 'https://api.example.com',act: 'someAction'
};export class DefaultInterceptor implements NetworkInterceptor {beforeRequest(request: RequestOptions, httprequest: http.HttpRequest): void | Promise<void> {LibLogManager.getLogger().info(TAG,'request: ' + JSONUtil.beanToJsonStr(request));httprequest.on('headersReceive', (header) => {LibLogManager.getLogger().info(TAG,'header: ' + JSONUtil.beanToJsonStr(header));});}afterResponse(response: http.HttpResponse, request: RequestOptions, httprequest: http.HttpRequest): void | Promise<void> {httprequest.off('headersReceive');LibLogManager.getLogger().info(TAG,'response: ' + JSONUtil.beanToJsonStr(response));}onError(error: BaseError, request: RequestOptions, httprequest: http.HttpRequest): void | Promise<void> {httprequest.off('headersReceive');LibLogManager.getLogger().error(TAG,'error: ' + JSON.stringify(error));}}
五、核型网络层代码:网络服务的心脏
在本节中,我们将展示如何通过NetworkService
类,实现一个强大而灵活的网络请求处理机制。这个类集成了数据转换、参数附加、异常处理等所有核心功能。
import { NetworkInterceptor } from './interceptor/NetworkInterceptor';
import { http } from '@kit.NetworkKit';
import { LibNetworkStatus } from '../network/LibNetworkStatus';
import { LibLogManager } from '../LibLog';
import { BaseError, NetworkError, NetworkResponseError } from '../../exception/NetworkError';
import { NetworkServiceErrorConst } from './NetworkServiceErrorConst';
import { Application } from '../../app/Application'
import { HashMap } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { DataConverter } from './converter/DataConverter';
import { QueryParamAppender } from './appender/QueryParamAppender';
import { CustomQueryParamAppender } from './appender/CustomQueryParamAppender';// 1、创建RequestOption.ets 配置类
export interface RequestOptions {baseUrl?: string;act?: string;method?: RequestMethod; // default is GETqueryParams ?: Map<string, number|string|boolean|Array<number> | Array<string> | Array<boolean> >;header?: Record<string,string> | Map<string,string> | HashMap<string,string>;extraData?: string | Object | ArrayBuffer;expectDataType?: http.HttpDataType;usingCache?: boolean;priority?: number;connectTimeout?: number;readTimeout?: number;multiFormDataList?:Array<http.MultiFormData>;
}export enum RequestMethod {OPTIONS = "OPTIONS",GET = "GET",HEAD = "HEAD",POST = "POST",PUT = "PUT",DELETE = "DELETE",TRACE = "TRACE",CONNECT = "CONNECT"
}export class NetworkService {baseUrl:string;constructor(baseUrl: string) {this.baseUrl = baseUrl;}private _dataConverter?: DataConverter | undefined; // 指定转换器public set dataConverter(value: DataConverter | undefined) {this._dataConverter = value;}private _queryParamAppender: QueryParamAppender = new CustomQueryParamAppender(); // 指定查询参数附加规则public set queryParamAppender(value: QueryParamAppender) {this._queryParamAppender = value;}private interceptors: NetworkInterceptor[] = [];addInterceptor(interceptor: NetworkInterceptor): void {this.interceptors.push(interceptor);}async request(requestOption: RequestOptions): Promise<http.HttpResponse> {let response: http.HttpResponse | null = null;let error: BaseError | null = null;// 每一个httpRequest对应一个HTTP请求任务,不可复用let httpRequest = http.createHttp();//开始发请求try {//如果url是传入的,则用传入的urlrequestOption.baseUrl = requestOption.baseUrl?requestOption.baseUrl:this.baseUrl;// 调用拦截器的beforeRequest方法for (const interceptor of this.interceptors) {await interceptor.beforeRequest(requestOption, httpRequest);}let url = requestOption.baseUrl + requestOption.act;if (this._queryParamAppender) {let param = this._queryParamAppender.append(requestOption.queryParams);if(param){url = url + "?" + param}}// 使用转换器转换请求数据if (this._dataConverter && requestOption.extraData) {requestOption.extraData = this._dataConverter.requestConvert(requestOption.extraData);}if(requestOption.baseUrl === null || requestOption.baseUrl.trim().length === 0){throw NetworkServiceErrorConst.getNetworkError(NetworkServiceErrorConst.URL_NOT_EXIST_ERROR)}if (!LibNetworkStatus.getInstance().isNetworkAvailable()) {LibLogManager.getLogger().error("HttpCore","网络不可用")throw NetworkServiceErrorConst.getNetworkError(NetworkServiceErrorConst.UN_AVILABLE)}if (!this.isValidUrl(requestOption.baseUrl)) {LibLogManager.getLogger().error("HttpCore","url格式不合法")throw NetworkServiceErrorConst.getNetworkError(NetworkServiceErrorConst.URL_ERROR)}let defalutHeader :Record<string,string> = {'Content-Type': 'application/json'}let expectDataType = requestOption.expectDataType||http.HttpDataType.STRING;response = await httpRequest.request(url , {method: requestOption.method,header: requestOption.header || defalutHeader,extraData: requestOption.extraData, // 当使用POST请求时此字段用于传递内容expectDataType: expectDataType, // 可选,指定返回数据的类型usingCache: requestOption.usingCache, // 可选,默认为truepriority: requestOption.priority, // 可选,默认为1connectTimeout: requestOption.connectTimeout, // 可选,默认为60000msreadTimeout: requestOption.readTimeout, // 可选,默认为60000msmultiFormDataList: requestOption.multiFormDataList,})if (http.ResponseCode.OK !== response.responseCode) {throw new NetworkResponseError(response.responseCode, NetworkServiceErrorConst.getErrorReason(response.responseCode))}// 使用转换器转换响应数据if (response && this._dataConverter) {response.result = this._dataConverter.responseConvert(response.result, expectDataType);}// 调用拦截器的afterResponse方法for (const interceptor of this.interceptors) {await interceptor.afterResponse(response, requestOption, httpRequest);}} catch (e) {if(e instanceof NetworkResponseError || e instanceof NetworkError){error = e;} else {let err = e as BusinessError;error = NetworkServiceErrorConst.getNetworkError(err.code)}}// 根据是否有错误来调用拦截器的afterResponse或onError方法if (error) {for (const interceptor of this.interceptors) {await interceptor.onError(error, requestOption, httpRequest);}httpRequest.destroy();throw error; // 重新抛出错误以便调用者可以处理} else{httpRequest.destroy();return response!;}}private isValidUrl(url: string): boolean {// 正则表达式匹配 URLconst urlPattern = new RegExp('^(https?:\/\/)?' + // protocol'((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|' + // domain name'((\d{1,3}\.){3}\d{1,3}))' + // OR ip (v4) address'(\:\d+)?(\/[-a-z\d%_.~+]*)*' + // port and path'(\?[;&a-z\d%_.~+=-]*)?' + // query string'(\#[-a-z\d_]*)?$', // fragment locator'i' // ignore case);return urlPattern.test(url);}}
结语
本文深入探讨了网络层的封装与优化,从数据转换到错误处理,每一步都体现了构建高效网络服务的艺术。希望这些实践能够帮助开发者在鸿蒙开发中游刃有余,构建出更加健壮和用户友好的应用。