当前位置: 首页 > news >正文

鸿蒙实战开发:网络层的艺术——优雅封装与搭建指南(中)

前言

在鸿蒙开发的广袤天地中,网络层的搭建与封装无疑是构建高效、稳定应用的基石。继上篇的探索之后,本文将继续深入网络层的优化之旅,揭秘如何通过类型转换器、请求查询附加器以及丰富的常量参数,将网络层的构建艺术推向一个新的高度。

一、网络请求的深度优化

数据类型转换器:定义与实践

在网络请求的世界里,数据格式的转换至关重要。我们通过定义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;}}

三、异常定义:清晰的错误处理策略

我们重新封装了网络请求错误,定义了BaseErrorNetworkError等类,使得错误类型一目了然,便于开发者快速定位问题。


// 自定义错误类型
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);}}

结语

本文深入探讨了网络层的封装与优化,从数据转换到错误处理,每一步都体现了构建高效网络服务的艺术。希望这些实践能够帮助开发者在鸿蒙开发中游刃有余,构建出更加健壮和用户友好的应用。

相关文章:

鸿蒙实战开发:网络层的艺术——优雅封装与搭建指南(中)

前言 在鸿蒙开发的广袤天地中&#xff0c;网络层的搭建与封装无疑是构建高效、稳定应用的基石。继上篇的探索之后&#xff0c;本文将继续深入网络层的优化之旅&#xff0c;揭秘如何通过类型转换器、请求查询附加器以及丰富的常量参数&#xff0c;将网络层的构建艺术推向一个新…...

docker in docker 连私有仓库时报错 https

背景 jenkins 是使用 docker 方式部署的, 在 jenkins中又配置了 docker 的命令, 使用的宿主机的 docker 环境, 在jenkins 中执行 docker 相关命令的时候报错 jenkinse0e7b943b6e4:/$ docker login -u admin -p Harbor12345 172.16.100.15:80 WARNING! Using --password via t…...

mac怎么压缩pdf文件,苹果电脑怎么压缩pdf文件大小

在当今数字化时代&#xff0c;PDF文件已成为广泛使用的文档格式之一。然而&#xff0c;PDF 文件可能会因其包含的图像、图形和其他元素而导致文件较大&#xff0c;这可能会影响文件的传输、存储和共享。因此&#xff0c;对 PDF 文件进行压缩以减小其文件大小是很有必要的。今天…...

兴顺物流管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;驾驶员管理&#xff0c;物流资讯管理&#xff0c;车辆管理&#xff0c;基础数据管理 员工账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;物流资讯管理&…...

力扣(2024.06.21)

1. 54——螺旋矩阵 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 标签&#xff1a;数组&#xff0c;矩阵&#xff0c;模拟 代码&#xff1a; class Solution:def spiralOrder(self, matrix: List[List[int]]) -&…...

飞机大战java

"飞机大战"是一种经典的射击游戏&#xff0c;通常在各种平台上都有实现&#xff0c;包括Java。如果你想要开发一个Java版本的飞机大战游戏&#xff0c;你可能需要考虑以下几个方面&#xff1a; 游戏设计&#xff1a;确定游戏的基本规则&#xff0c;比如玩家控制的飞机…...

Springboot的自动配置原理

文章目录 Springboot的自动配置原理?1. Spring Boot Starter 依赖2.SpringBootApplication注解3.自动触发配置4.Auto-configuration Classes5.条件注解6. 外部配置文件7. 优先级和排除总结 Springboot的自动配置原理? 1. Spring Boot Starter 依赖 Spring Boot 提供了各种 …...

Interview preparation--elascitSearch深分页问题

深度分页出现原因 当我们需要查询的数据页数特别大的时候&#xff0c;比如from size 大于10000 的时候&#xff0c;可能出现“window is too large” 异常&#xff0c;如下网图&#xff1a; 查询语句如下 { "query": { "bool": { "must": [ {…...

C语言笔试题:实现把一个无符号整型数字的二进制序列反序后输出

目录 题目 实例 方法一&#xff1a;直接交换 方法二&#xff1a;间接交换 拓展 题目 编写一个函数&#xff0c;将一个无符号整数的所有位逆序&#xff08;在32位机器下&#xff09; 实例 例如有一个无符号整数 unsigned int num 32; unsigned int 在32位系统中占4个字…...

elementplus如何实现dialog遮罩层外的元素可以被操作点击

elementplus如何实现dialog遮罩层外的元素可以被操作点击 element plus 组件库中的 dialog 组件可以说是使用频率最高的组件之一&#xff0c;它的效果是弹出一个对话框&#xff0c;外面默认会有一个蒙层。 现在我碰到的需求是&#xff0c;弹窗要正常显示&#xff0c;但是蒙层下…...

Springboot整合Kafka消息队列服务实例

一、Kafka相关概念 1、关于Kafka的描述 Kafka是由Apache开源&#xff0c;具有分布式、分区的、多副本的、多订阅者&#xff0c;基于Zookeeper协调的分布式处理平台&#xff0c;由Scala和Java语言编写。通常用来搜集用户在应用服务中产生的动作日志数据&#xff0c;并高速的处…...

kotlin——MVVM框架下的大型项目优化

目录 概要 优化思路 一、重构过长的Activity 二、优化臃肿的ViewModel 示例代码&#xff1a; 概要 在大型项目中&#xff0c;随着项目越做越大&#xff0c;activity和viewmodel的代码会越来越多&#xff0c;尽量保持Activity和ViewModel的代码精简和易于维护是非常重要的。个人…...

echarts实现折线图点击添加标记

文章目录 背景一、代码示例 背景 业务场景体现在功能层面主要两点&#xff0c; 折线图表设置点击事件点击事件与图标渲染标记绑定 对于节点没有被添加标记的可以&#xff0c;弹框提示添加标记&#xff0c;并提供标记内容输入框&#xff0c;已经添加过标记的点&#xff0c;点…...

循环赛日程表

描述 n 2 ^ k个选手 每个选手必须与其他n-1个选手各赛一次每个选手一天赛一次比赛打n-1天 思路 k 3时的解 我们先进行假设&#xff1a;每个选手第一天和自己比&#xff0c;然后分解成4个子问题&#xff1a; (1)14号的第14天&#xff0c;对手1~4号; (2)14号的第58天&a…...

计算机网络:运输层 - 概述

计算机网络&#xff1a;运输层 - 概述 运输层的任务端口号复用与分用UDP协议首部格式 TCP协议面向字节流 运输层的任务 物理层、数据链路层以及网络层&#xff0c;他们共同解决了将主机通过网络互联起来所面临的问题&#xff0c;实现了主机到主机的通信。 网络层的作用范围是…...

使用阿里开源的Spring Cloud Alibaba AI开发第一个大模型应用

背景 前段时间看到Spring推出了SpringAI&#xff0c;可以方便快速的接入ChatGPT等国外的大模型&#xff0c;现在阿里巴巴也紧追脚步推出了Spring Cloud Alibaba AI&#xff0c;Spring Cloud Alibaba AI 目前基于 Spring AI 0.8.1 版本 API 完成通义系列大模型的接入。通义接入…...

`THREE.PointsMaterial` 是 Three.js 中用于创建粒子系统材质的类。它允许你设置粒子系统的外观属性,比如颜色、大小和透明度。

demo案例 THREE.PointsMaterial 是 Three.js 中用于创建粒子系统材质的类。它允许你设置粒子系统的外观属性&#xff0c;比如颜色、大小和透明度。下面是对其构造函数的参数、属性和方法的详细讲解。 构造函数 const material new THREE.PointsMaterial(parameters);参数&am…...

Android-Android Studio-FAQ

1 需求 2 接口 3 Android Studio xml布局代码补全功能失效问题 最终解决方案就是尝试修改compileSdk 为不同SDK版本来解决问题&#xff0c;将原本34修改为32测试会发现xml代码补全功能有效了&#xff01; 参考资料 Android Studio xml布局代码补全功能失效问题_android studi…...

架构师指南:现代 Datalake 参考架构

这篇文章的缩写版本于 2024 年 3 月 26 日出现在 The New Stack 上。 旨在最大化其数据资产的企业正在采用可扩展、灵活和统一的数据存储和分析方法。这一趋势是由企业架构师推动的&#xff0c;他们的任务是制定符合不断变化的业务需求的基础设施。现代数据湖体系结构通过将数…...

通讯协议大全(UART,RS485,SPI,IIC)

参考自&#xff1a; 常见的通讯协议总结&#xff08;USART、IIC、SPI、485、CAN&#xff09;-CSDN博客 UART那么好用&#xff0c;为什么单片机还需要I2C和SPI&#xff1f;_哔哩哔哩_bilibili 5分钟看懂!串口RS232 RS485最本质的区别&#xff01;_哔哩哔哩_bilibili 喜欢几位…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...