HarmonyOS 鸿蒙Next中http设置请求头失败
HarmonyOS 鸿蒙Next中http设置请求头失败 有个本地端业务,使用第三方库的GitHub · Where software is built webserver库实现的后端和,现在有个问题出现 我使用无论是第三方的axios还有鸿蒙原生的http都会出现 后端接收不到请求headers 还有 body,但是去请求JAVA或者C#等部署的后端服务器可以正常接收到 ,我是用js版本的axios还有postman去请求webserver的后端也是正常的能看到请求头,有知道怎么解决这个问题的吗,附上我的http代码
import { http } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { FontLog } from "../utils/FontLog";
import { GlobalPreferences } from '../common/PreferencesConfig';
import { JSON } from '@kit.ArkTS';
let mlog = new FontLog("axios");
const TOKEN_KEY = 'auth_token';
const DEFAULT_TOKEN = 'IP63mOM4Pe1dE/ONgJAYuAu9nGCsqWCYDbOPc7QV35wymg0yTFHTqeEGBYAoYR2z';
interface ResponseData {
code?: number;
data?: ESObject;
message?: string;
}
interface ProxyConfig {
host: string;
port: number;
username?: string;
password?: string;
}
interface HttpClientConfig {
baseURL: string;
timeout: number;
headers: ESObject;
proxy?: ProxyConfig; // 代理配置
usingProxy?: boolean; // 是否使用代理
}
export interface HttpResponse<T> {
data: T;
status: number;
statusText: string;
headers: ESObject;
config: HttpClientConfig;
}
function getToken(): string {
try {
const gp = GlobalPreferences.getInstance();
const token = gp.getSync<string>(TOKEN_KEY, DEFAULT_TOKEN);
return token;
} catch (error) {
return DEFAULT_TOKEN;
}
}
function buildURL(baseURL: string, url: string, params?: ESObject): string {
let fullURL = baseURL;
if (url) {
if (url.startsWith('http://') || url.startsWith('https://')) {
fullURL = url;
} else {
// 移除 baseURL 末尾的斜杠
const base = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL;
// 移除 url 开头的斜杠
const path = url.startsWith('/') ? url : '/' + url;
fullURL = base + path;
}
}
if (params) {
const keys = Object.keys(params);
if (keys.length > 0) {
const queryParams: string[] = [];
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value: ESObject = params[key] as ESObject;
if (value !== undefined && value !== null) {
queryParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
}
}
if (queryParams.length > 0) {
fullURL += (fullURL.indexOf('?') > -1 ? '&' : '?') + queryParams.join('&');
}
}
}
return fullURL;
}
function mergeHeaders(defaultHeaders: ESObject, configHeaders?: ESObject): Record<string, string> {
const merged: Record<string, string> = {};
if (defaultHeaders) {
const defaultKeys = Object.keys(defaultHeaders);
for (let i = 0; i < defaultKeys.length; i++) {
const key = defaultKeys[i];
const value: ESObject = defaultHeaders[key] as ESObject;
// 确保所有值都是字符串类型
merged[key] = value !== undefined && value !== null ? String(value) : '';
}
}
if (configHeaders) {
const configKeys = Object.keys(configHeaders);
for (let i = 0; i < configKeys.length; i++) {
const key = configKeys[i];
const value: ESObject = configHeaders[key] as ESObject;
// 确保所有值都是字符串类型,并覆盖默认值
merged[key] = value !== undefined && value !== null ? String(value) : '';
}
}
return merged;
}
interface RequestInterceptorResult {
url: string;
headers: Record<string, string>;
data?: string;
}
function requestInterceptor(config: HttpClientConfig, url: string, data?: ESObject, params?: ESObject,
headers?: ESObject): RequestInterceptorResult {
const token = getToken();
const mergedHeaders: Record<string, string> = mergeHeaders(config.headers, headers);
// 确保所有 header 值都是字符串类型
mergedHeaders['Content-Type'] = 'application/json;charset=UTF-8';
mergedHeaders['Authorization'] = `Bearer ${token}`;
mergedHeaders['authorization'] = `Bearer ${token}`;
const fullURL = buildURL(config.baseURL, url, params);
// 构建固定格式的请求体:{token, data: {}}
const requestBody: ESObject = {
token: token,
data: data || {}
};
const requestData: string = JSON.stringify(requestBody);
const requestInfo: ESObject = {
method: 'POST',
url: fullURL,
body: requestBody,
params: params,
headers: mergedHeaders
};
// 打印请求头(调试信息)
mlog.info(`[请求头] ${JSON.stringify(mergedHeaders)}`);
// 打印请求URL(调试信息)
mlog.info(`[请求URL] ${fullURL}`);
// 打印请求body(调试信息)
mlog.info(`[请求Body] ${requestData}`);
const result: RequestInterceptorResult = {
url: fullURL,
headers: mergedHeaders,
data: requestData
};
return result;
}
function responseInterceptor(response: http.HttpResponse, config: HttpClientConfig,
url: string): HttpResponse<ESObject> {
let responseData: ESObject = {};
try {
const resultValue: ESObject = response.result as ESObject;
if (resultValue && typeof resultValue === 'string') {
responseData = JSON.parse(resultValue) as ESObject;
} else if (resultValue && typeof resultValue === 'object') {
responseData = resultValue as ESObject;
}
} catch (error) {
const errorValue: Error = error as Error;
const errorObj: ESObject = {
message: errorValue.message || String(errorValue),
error: errorValue
};
mlog.error(`[响应解析错误] ${JSON.stringify(errorObj)}`);
}
const responseInfo: ESObject = {
status: response.responseCode,
statusText: '',
url: url,
method: 'POST',
data: responseData
};
const httpResponse: HttpResponse<ESObject> = {
data: responseData,
status: response.responseCode,
statusText: '',
headers: response.header || {},
config: config
};
if (response.responseCode === 200) {
if (responseData && typeof responseData === 'object') {
const data = responseData as ResponseData;
if (data.code !== undefined) {
if (data.code === 0 || data.code === 200) {
return httpResponse;
} else {
mlog.error(`[业务错误] code=${data.code}, message=${data.message || '未知错误'}`);
const error = new Error(`业务错误: code=${data.code}, message=${data.message || '未知错误'}`);
(error as ESObject)['response'] = httpResponse;
throw error;
}
}
}
return httpResponse;
} else {
mlog.error(`[响应错误] status=${response.responseCode}`);
const error = new Error(`响应错误: status=${response.responseCode}`);
(error as ESObject)['response'] = httpResponse;
throw error;
}
}
function makeRequest<T = ESObject>(
config: HttpClientConfig,
url: string,
data?: ESObject,
params?: ESObject,
headers?: ESObject
): Promise<HttpResponse<T>> {
return new Promise((resolve, reject) => {
// 每一个httpRequest对应一个HTTP请求任务,不可复用
const httpRequest = http.createHttp();
const requestConfig = requestInterceptor(config, url, data, params, headers);
// 用于订阅HTTP响应头,此接口会比request请求先返回。可以根据业务需要订阅此消息
// 从API 8开始,使用on('headersReceive', Callback)替代on('headerReceive', AsyncCallback)。 8+
httpRequest.on('headersReceive', (header) => {
mlog.info(`[响应头] ${JSON.stringify(header)}`);
});
// 使用 addHeader 方法添加请求头
const headerKeys = Object.keys(requestConfig.headers);
for (let i = 0; i < headerKeys.length; i++) {
const key = headerKeys[i];
const value = requestConfig.headers[key];
if (value !== undefined && value !== null) {
// 使用类型断言,因为类型定义可能不完整,但运行时支持此方法
(httpRequest as ESObject).addHeader(key, String(value));
}
}
// 所有请求都使用 POST 方法
// 构建请求选项
const httpRequestOptions: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
readTimeout: config.timeout,
connectTimeout: config.timeout,
expectDataType: http.HttpDataType.STRING,
usingCache: true,
priority: 1,
extraData: requestConfig.data, // 请求体数据
usingProxy: config.usingProxy !== undefined ? config.usingProxy : false // 是否使用代理
};
// 如果配置了代理,记录代理信息(鸿蒙系统会自动使用系统代理配置)
if (config.proxy && config.usingProxy) {
mlog.info(`[代理配置] host=${config.proxy.host}, port=${config.proxy.port}`);
// 注意:鸿蒙的 HTTP 请求会使用系统级别的代理配置
// 如果需要自定义代理,可能需要通过系统网络设置或使用其他网络库
}
httpRequest.request(requestConfig.url, httpRequestOptions,
(err: BusinessError | null, response: http.HttpResponse | null) => {
if (err) {
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,务必调用destroy方法主动销毁
httpRequest.destroy();
const errObj: ESObject = err as ESObject;
const errorInfo: ESObject = {
message: (errObj['message'] as string) || JSON.stringify(errObj),
code: (errObj['code'] as number) || 0,
url: requestConfig.url,
method: 'POST'
};
mlog.error(`[请求失败] POST ${errorInfo.url}`);
mlog.error(`[错误信息] ${JSON.stringify(errorInfo)}`);
reject(errObj);
return;
}
if (!response) {
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,务必调用destroy方法主动销毁
httpRequest.destroy();
const error: ESObject = {
message: '响应为空',
url: requestConfig.url,
method: 'POST'
};
reject(error);
return;
}
try {
const httpResponse: HttpResponse<ESObject> = responseInterceptor(response, config, url);
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,务必调用destroy方法主动销毁
httpRequest.destroy();
resolve(httpResponse as HttpResponse<T>);
} catch (error) {
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,务必调用destroy方法主动销毁
httpRequest.destroy();
const errorValue: Error = error as Error;
const errorObj: ESObject = {
message: errorValue.message || String(errorValue),
error: errorValue
};
reject(errorObj);
}
});
});
}
export interface HttpRequestConfig {
url: string;
data?: ESObject; // 传入的对象,会被封装到 {token, data: {}} 的 data 字段中
params?: ESObject; // URL 查询参数
headers?: ESObject; // 额外的请求头
}
class AxiosHttpRequest {
private config: HttpClientConfig;
constructor(config: HttpClientConfig) {
this.config = config;
}
/**
* 发送 POST 请求
* 请求体格式固定为:{token, data: {}}
* token 会自动从配置中获取并封装
* @param requestConfig 请求配置
* @returns Promise<HttpResponse<T>>
*/
post<T = ESObject>(requestConfig: HttpRequestConfig): Promise<HttpResponse<T>> {
// 如果传入的 data 是一个对象,直接使用;如果是其他格式,包装成对象
let requestData: ESObject = {};
if (requestConfig.data) {
// 如果 data 已经是对象,直接使用
if (typeof requestConfig.data === 'object' && requestConfig.data !== null) {
requestData = requestConfig.data;
} else {
// 如果是其他类型,包装成对象
requestData = { value: requestConfig.data };
}
}
return makeRequest<T>(
this.config,
requestConfig.url,
requestConfig.params,
requestConfig.headers,
requestData // 这个 data 会被封装到 {token, data: {}} 的 data 字段中
);
}
}
type HttpPromise<T> = Promise<HttpResponse<T>>;
const axiosClientConfig: HttpClientConfig = {
baseURL: "http://localhost:8080/pk",
timeout: 10 * 1000,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Authorization': "Bearer IP63mOM4Pe1dE/ONgJAYuAu9nGCsqWCYDbOPc7QV35yeDR3teLPaxA222BRXgTZS"
},
// 代理配置(可选)
// 启用代理:设置为 true 以使用系统代理配置
usingProxy: false,
// 自定义代理配置(如果需要)
proxy: {
host: 'http://192.168.1.83:8080/pk',
port: 8081,
}
};
const axiosClient = new AxiosHttpRequest(axiosClientConfig);
export { axiosClient };
export type { HttpPromise };
更多关于HarmonyOS 鸿蒙Next中http设置请求头失败的实战教程也可以访问 https://www.itying.com/category-93-b0.html
请求头合并逻辑优化
function mergeHeaders(...) {
// 确保合并时保留原始Headers的完整性
const merged: Record<string, string> = { ...defaultHeaders };
// 覆盖逻辑改为保留已有值
if (configHeaders) {
for (const key in configHeaders) {
if (configHeaders[key] !== undefined) {
merged[key] = String(configHeaders[key]);
}
}
}
return merged;
}
请求体构建策略调整
// 移除强制嵌套结构,根据后端要求选择直接传递data
const requestBody: ESObject = data || {};
const requestData: string = JSON.stringify(requestBody);
试试
更多关于HarmonyOS 鸿蒙Next中http设置请求头失败的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
你好,定位到是下面代码问题, httpRequest 并没有 addHeader 方法,所以这里调用时抛出了异常。导致后面代码没有执行,请求没有发出去。
const headerKeys = Object.keys(requestConfig.headers);
for (let i = 0; i < headerKeys.length; i++) {
const key = headerKeys[i];
const value = requestConfig.headers[key];
if (value !== undefined && value !== null) {
// 使用类型断言,因为类型定义可能不完整,但运行时支持此方法
(httpRequest as ESObject).addHeader(key, String(value));
}
}
添加自定义header,是在http.HttpRequestOptions里面。
const httpRequestOptions: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
readTimeout: config.timeout,
connectTimeout: config.timeout,
...
header: header //你的headers
};
在HarmonyOS Next中,HTTP请求头设置失败通常与网络权限配置或API使用方式有关。请检查以下两点:
-
网络权限:确保在
module.json5文件中已正确声明ohos.permission.INTERNET权限。 -
API使用:使用
@ohos.net.http模块时,需通过http.createHttp()创建请求对象,然后使用request.setHeader()方法设置请求头。确保在调用request.request()发起请求前完成设置。
示例代码片段:
let httpRequest = http.createHttp();
httpRequest.setHeader('Content-Type', 'application/json');
// ... 其他设置
httpRequest.request(url, options, (err, data) => {});
检查代码逻辑,确保设置请求头的步骤未被跳过或覆盖。
根据你的描述和代码,问题可能出在以下几个方面:
-
请求头设置方式问题:在HarmonyOS Next的@kit.NetworkKit中,设置请求头应该使用
httpRequestOptions.header字段,而不是addHeader方法。你的代码中使用了类型断言调用addHeader,这可能不是标准用法。 -
请求体格式问题:你的代码将请求体构建为
{token, data: {}}格式,但实际发送时使用的是extraData字段。需要确认WebServer后端期望的请求体格式。
建议修改如下:
// 修改请求选项配置,使用header字段设置请求头
const httpRequestOptions: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
readTimeout: config.timeout,
connectTimeout: config.timeout,
expectDataType: http.HttpDataType.STRING,
usingCache: true,
priority: 1,
extraData: requestConfig.data,
usingProxy: config.usingProxy !== undefined ? config.usingProxy : false,
// 正确设置请求头
header: requestConfig.headers
};
// 删除addHeader循环,直接使用上面的header配置
httpRequest.request(requestConfig.url, httpRequestOptions, ...);
-
Content-Type重复设置:你在代码中多次设置了Content-Type,确保最终使用的是正确的值。
-
本地网络访问权限:确认应用已申请网络权限,在
module.json5中添加:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
-
WebServer兼容性:由于WebServer是第三方库,可能需要检查它是否对HarmonyOS的HTTP客户端有特殊要求。可以尝试在WebServer端打印原始请求信息来调试。
-
使用系统原生方式:考虑直接使用@kit.NetworkKit的标准示例代码,避免过多的封装层,以排除代码逻辑问题。
建议先简化代码,使用最基本的HTTP请求测试WebServer是否能正常接收请求头和请求体,再逐步添加业务逻辑。

