当前位置: 首页 > 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 喜欢几位…...

基于EXCEL数据表格创建省份专题地图

1 数据源 随着西藏于5月1日发布2022年一季度经济运行情况&#xff0c;31省份一季度GDP数据已全部出炉。 总量方面&#xff0c;粤苏鲁稳居前三&#xff1b;增速方面&#xff0c;23省份高于“全国线”&#xff0c;新疆表现最佳&#xff0c;增速达到7.0%。 表格表现数据不够直观…...

基于java+springboot+vue实现的电商应用系统(文末源码+Lw)241

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本电商应用系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&a…...

好文!12个策略解决 Kafka 数据丢失问题

哥们儿&#xff01;有遇到Kafka数据丢失问题的问题吗&#xff0c;你是如何解决的&#xff1f;今天的文章&#xff0c;V哥来详细解释一下&#xff0c;整理了12种解决策略&#xff0c;希望可以帮助你解决项目中的问题&#xff1a;以下是一些常见的解决方案和最佳实践。 生产者确认…...

Android 第三方框架:网络:OkHttp:源码分析:拦截器

文章目录 涉及到的设计模式 责任链模式:ArrayList策略模式:Interceptor和XXXInterceptor源码分析API总结涉及到的设计模式 责任链模式:ArrayList ArrayList 用ArrayList作为保存顺序的数据结构 把系统提供的各种Interceptor和自定义的Interceptor放入ArrayList中 RealI…...

FlowUs AI的使用教程和使用体验

FlowUs AI 使用教程 FlowUs AI特点使其成为提升个人和团队生产力的有力工具&#xff0c;无论是在学术研究、内容创作、技术开发还是日常办公中都能发挥重要作用。现在来看看如何使用FlowUs AI吧&#xff01; 注册与登录&#xff1a;首先&#xff0c;确保您已经注册并登录FlowU…...

SwiftUI 6.0(iOS 18)ScrollView 全新的滚动位置(ScrollPosition)揭秘

概览 在只有方寸之间大小的手持设备上要想体面的向用户展示海量信息&#xff0c;滚动视图&#xff08;ScrollView&#xff09;无疑是绝佳的“东牀之选”。 在 SwiftUI 历史的长河中&#xff0c;总觉得苹果对于 ScrollView 视图功能的升级是在“挤牙膏”。这不&#xff0c;在本…...

阿贝云免费虚拟主机和免费云服务器评测

阿贝云是一家提供免费虚拟主机和免费云服务器的服务商&#xff0c;为用户提供了一个便捷的搭建网站和应用的平台。他们的服务受到了很多用户的好评。用户可以轻松地在阿贝云上创建自己的网站&#xff0c;并享受免费的虚拟主机和云服务器。通过阿贝云的服务&#xff0c;用户可以…...

不懂就问,开通小程序地理位置接口有那么难吗?

小程序地理位置接口有什么功能&#xff1f; 若提审后被驳回&#xff0c;理由是“当前提审小程序代码包中地理位置相关接口( chooseAddress、getLocation )暂未开通&#xff0c;建议完成接口开通后或移除接口相关内容后再进行后续版本提审”&#xff0c;那么遇到这种情况&#x…...

Python 全栈系列256 异步任务与队列消息控制(填坑)

说明 每个创新都会伴随着一系列的改变。 在使用celery进行异步任务后&#xff0c;产生的一个问题恰好也是因为异步产生的。 内容 1 问题描述 我有一个队列 stream1, 对应的worker1需要周期性的获取数据&#xff0c;对输入的数据进行模式识别后分流。worker1我设施为10秒运行…...

从零开始的Ollama指南:部署私域大模型

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 大模型应用向开发路径&#xff1a;AI代理工作流大模型应用开发实用开源项目汇总大模…...

建设一个网站的工作方案/关键词优化平台有哪些

刚才安装notebook插件jupyter_contrib_nbextensions&#xff0c;搜了很多教程都没有作用。直到用了这个命令&#xff0c;一行解决。 pip install jupyter_contrib_nbextensions && jupyter contrib nbextension install 打开打开 Jupyter Notebook&#xff0c;可以看到…...

网站开发遵循/免费的外贸b2b网站

45.买股票的最佳时机 题目描述 假设你有一个数组&#xff0c;其中第\ i i 个元素是股票在第\ i i 天的价格。 你有一次买入和卖出的机会。&#xff08;只有买入了股票以后才能卖出&#xff09;。请你设计一个算法来计算可以获得的最大收益。 输入 [1,4,2]返回值 3输入 [2…...

装修公司怎么做免费网站/b站是哪个网站

1、问题 javaweb工程中都有web.xml文件。 那么web.xml的作用是什么呢&#xff1f;每个web工程中web.xml都必须的吗&#xff1f; 2、前言 一个web中可以没有web.xml文件&#xff0c;也就是说&#xff0c;web.xml文件并不是web工程必须的。 web.xml文件是用来初始化配置信息…...

帮一个公司做网站多少钱/超级优化

目前&#xff0c;信息流短视频排序是基于CTR预估Wide&Deep深层模型。在Wide&Deep模型基础上做一系列相关优化&#xff0c;包括相关性与体感信号引入、多场景样本融合、多模态学习、树模型等&#xff0c;均取得不错收益。 总体上&#xff0c;短视频模型优化可分为两部分…...

广州外贸营销型网站建设公司/精准营销通俗来说是什么

GitHub 18k Star 的Java工程师成神之路&#xff0c;不来了解一下吗&#xff01; GitHub 18k Star 的Java工程师成神之路&#xff0c;真的不来了解一下吗&#xff01; GitHub 18k Star 的Java工程师成神之路&#xff0c;真的真的不来了解一下吗&#xff01; 相信很多人对Java中的…...

域名持有者个人可以做公司网站/口碑营销的特点

VMware Horizon View 从 6.2 开始支持链接克隆自动场。在此之前&#xff0c;用户只能创建手动场。有了这个功能&#xff0c;可以省去用户很多手动步骤&#xff0c;提高管理效率&#xff0c;节约时间成本。本博客演示创建链接克隆自动场的步骤。 一. 创建Windows 2008R2 SP1父虚…...