axios AxiosHeaders 请求头方法全集:从 set/get 到 normalize 的源码级实践指南
2026/9/7 9:50:50 网站建设 项目流程

axios AxiosHeaders 请求头方法全集:从 set/get 到 normalize 的源码级实践指南

【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios

本篇基于 axios 官方文档《Métodos de encabezados》(header-methods.md)展开,系统讲解AxiosHeaders类的全部核心方法——构造、setgethasdeleteclearnormalizeconcattoJSONtoString、静态from以及内建快捷访问器。读完后你将掌握:如何以比直接操作普通对象更安全、更可控的方式管理 HTTP 请求头,以及每个方法在 lib/core/AxiosHeaders.js 中的底层实现逻辑与典型调用链。

为什么需要 AxiosHeaders:先看懂构造器

自引入AxiosHeaders类以来,axios 提供了一组专门用于操作请求头的方法。相比直接摆弄一个普通对象,AxiosHeaders在键名大小写归一、重复键合并、头部值清洗等方面做了统一处理,是"更便捷的设置、获取与删除请求头方式"的载体。

类定义在 lib/core/AxiosHeaders.js,构造器签名如下:

constructor(headers?: RawAxiosHeaders | AxiosHeaders | string);

它接受一个可选参数用于初始化实例:可以是任意数量的头对象(键名大小写不敏感),也可以是一段以换行符分隔的原始头部文本。源码中构造器只做了一件事——把参数交给set

constructor(headers) { headers && this.set(headers); }

这解释了为什么构造函数、set单键值、set对象、set原始字符串三种用法的行为完全一致。传入原始字符串时,set内部会走 parseHeaders.js 的解析逻辑(按行切分、冒号左侧为键并转小写、右侧为值),例如:

const headers = new AxiosHeaders(` Host: www.bing.com User-Agent: curl/7.54.0 Accept: */*`); console.log(headers); // Object [AxiosHeaders] { // host: 'www.bing.com', // 'user-agent': 'curl/7.54.0', // accept: '*/*' // }

值得注意的是,parseHeaders内置了一张ignoreDuplicateOf列表(lib/helpers/parseHeaders.js#L7-L25),涵盖hostcontent-typeuser-agent等 Node.js HTTP 模块会忽略重复项的头部——解析原始文本时这些键只保留第一个值,而set-cookie会被收集为数组,其余重复键以', '拼接。这一行为是从原始报文解析场景出发的刻意设计。

set:三种输入形态与 rewrite 覆盖语义

set是写入请求头的主入口,支持三种调用形态以及一个控制覆盖行为的可选参数rewrite

set(headerName, value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher); set(headerName, value, rewrite?: (this: AxiosHeaders, value: string, name: string) => boolean); set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean); set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean);

参数rewrite控制覆盖行为,语义如下:

rewrite 取值行为
false若该头已有值(非undefined)则不覆盖
undefined(默认)会覆盖,除非现有值被显式设置为false
true无条件覆盖

也可以传入用户自定义函数,由它决定是否覆盖——函数接收当前值、头名称以及头对象本身。另外,空名称或纯空格名称会被直接忽略。

源码中的判定逻辑非常直观(lib/core/AxiosHeaders.js#L206-L222):

function setHeader(_value, _header, _rewrite) { const lHeader = normalizeHeader(_header); // trim + toLowerCase if (!lHeader) { return; // 空名或纯空格 -> 忽略 } const key = utils.findKey(self, lHeader); // 查找已存在的同名键(保留原大小写) if ( !key || self[key] === undefined || _rewrite === true || (_rewrite === undefined && self[key] !== false) ) { self[key || _header] = normalizeValue(_value); } }

这里有两个关键细节:

  • 大小写不敏感的键查找normalizeHeader对名称做trim().toLowerCase(),而实际写入时用utils.findKey找到实例上已存在的原始键名,因此Content-Typecontent-type指向同一个键。
  • false是"占位"语义:把某头显式设为false后,默认写入不再覆盖它,只有rewrite === true才能改回——单元测试 tests/unit/axiosHeaders.test.js#L48-L77 专门验证了这两条规则。

接受 Map 等可迭代键值对

set的第三个形态接受可迭代的键值对序列,如Map

const headers = new AxiosHeaders(); headers.set( new Map([ ['X-Trace-Id', 'abc123'], ['Accept', 'application/json'], ]) );

从源码结构看,可迭代分支会先把迭代结果归拢到一个 null-proto 对象,迭代项不是二元数组时会抛出TypeError('Object iterator must return a key-value pair')(lib/core/AxiosHeaders.js#L232-L251)。测试中还有针对性验证:即便Object.prototype被注入同名键或污染了Symbol.iterator,也不会把原型链上的值混入迭代来源(tests/unit/axiosHeaders.test.js#L87-L129)。

保留特定头部的大小写

AxiosHeaders保留第一个匹配键的大小写形式。你可以利用这一点:先用undefined预设一个键名,之后再写入值,从而固定某个头部的大小写。这一技巧在对接对头部大小写行为不规范的服务器时很有用,完整的defaultsaxios.create用法见 保留特定请求头大小写。

写入值的阶段还会经过normalizeValue与 sanitizeHeaderValue:字符串值会被剥离 C0 控制字符与 DEL(0x00–0x080x0A–0x1F0x7F)并去掉两端空格/水平制表符,数组值递归处理;falsenull原样保留。

get:匹配器与解析器(含 parseParameters)

get用于读取头部值,可传入头名称、可选的 matcher 或 parser;matcher 的默认值为true,parser 可以是用于从头部值中提取内容的正则:

get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue;

官方示例展示了四种典型用法:

const headers = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary=Asrf456BGe4h', }); console.log(headers.get('Content-Type')); // multipart/form-data; boundary=Asrf456BGe4h console.log(headers.get('Content-Type', true)); // 用 \s,;= 分隔符把字符串解析为键值对 // [Object: null prototype] { // 'multipart/form-data': undefined, // boundary: 'Asrf456BGe4h' // } const quotedHeaders = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary="a,b"', }); console.log({ ...quotedHeaders.get('Content-Type', AxiosHeaders.parseParameters), }); // { boundary: 'a,b' } console.log( headers.get('Content-Type', (value, name, headers) => { return String(value).replace(/a/g, 'ZZZ'); }) ); // multipZZZrt/form-dZZZtZZZ; boundZZZry=Asrf456BGe4h console.log(headers.get('Content-Type', /boundary=(\w+)/)?.[0]); // boundary=Asrf456BGe4h

对照源码(lib/core/AxiosHeaders.js#L259-L287)可以看到分派顺序:

  1. header先经normalizeHeader归一,再用utils.findKey找到实际键名——所以取值不区分大小写;
  2. 无 parser 直接返回值;
  3. parser === trueparseTokens,用正则/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g做轻量分词(注意它对带引号的内容不做处理);
  4. 函数 parser 以该实例为this调用;正则 parser 执行exec
  5. 其它类型直接抛TypeError('parser must be boolean|regexp|function')

parseParameters:更严格的 HTTP 参数解析器

AxiosHeaders.parseParameters是可选的解析器,针对 HTTP 归一化参数值:返回 null-proto 对象,参数名一律转小写;剥离带引号字符串两端的引号,解码转义的\"\\,并保留引号内的逗号/分号;对不带引号的值只去掉 RFC 定义的可选空白(空格与水平制表符)。

其实现是一个手写的字符级状态机(lib/core/AxiosHeaders.js#L92-L149):按"切换引号状态、按0x5c记录转义、在引号外遇到,;时切出参数片段;参数名需匹配parameterNameRE/^[!#$%&'*+\-.^_|~0-9A-Za-z]+$/`)才会被收录。

一个安全细节值得注意:解析器会跳过__proto__constructorprototype这三个"危险键名",避免通过构造恶意头部值向解析结果注入原型污染面(lib/core/AxiosHeaders.js#L115-L121)。而传true时仍走上面提到的parseTokens旧分词器,以保持向后兼容。

has:存在性判断

has(header: string, matcher?: AxiosHeaderMatcher): boolean;

当且仅当头已定义(值不是undefined)时返回true。源码中的判定(lib/core/AxiosHeaders.js#L289-L303)是key && this[key] !== undefined && (!matcher || matchHeaderValue(...))——所以把某头显式设为falsehas依然返回true,只有undefined才视为"不存在"。可选的 matcher 可进一步按值过滤。

delete 与 clear:删除与清空

delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean;

delete支持单个头名称或名称数组,可按值匹配器筛选;至少删除一个头时返回true

clear(matcher?: AxiosHeaderMatcher): boolean;

clear无参时清空全部头部;传入 matcher 时只删除匹配项——且此时 matcher 是拿头名称(而非值)做比较。这一点在源码里体现得很明确:clear调用matchHeaderValue时传入了第五个参数isHeaderNameFilter = true,使字符串/正则过滤器作用在键名上(lib/core/AxiosHeaders.js#L332-L346)。

匹配器支持三种形式:字符串(子串包含)、正则(test)、函数,统一由 lib/core/AxiosHeaders.js#L153-L171 的matchHeaderValue处理。

normalize:合并重复键,适配器与拦截器的收尾动作

如果你直接以属性方式修改过头部对象(如headers.Foo = '2'),可能出现同名但大小写不同的多个键。normalize会把重复键合并为一个。axios 在每次拦截器执行后内部都会调用它。formattrue时把头名转为小写并首字母大写(cOntEnt-type=>Content-Type),传false则保持原格式:

const headers = new AxiosHeaders({ foo: '1', }); headers.Foo = '2'; headers.FOO = '3'; console.log(headers.toJSON()); // [Object: null prototype] { foo: '1', Foo: '2', FOO: '3' } console.log(headers.normalize().toJSON()); // [Object: null prototype] { foo: '3' } console.log(headers.normalize(true).toJSON()); // [Object: null prototype] { Foo: '3' }

返回this以便链式调用。实现上(lib/core/AxiosHeaders.js#L348-L373)它遍历自身,对已出现过的归一化名称直接合并到已有键上,否则按format决定是否做formatHeadertrim().toLowerCase()后按单词首字母大写)重命名。

"拦截器后调用"这一点可以在仓库里直接找到证据:XHR、HTTP、fetch 三个适配器以及transformData都会先取头再normalize

  • lib/adapters/xhr.js#L21:const requestHeaders = AxiosHeaders.from(_config.headers).normalize();
  • lib/adapters/http.js#L766:const headers = AxiosHeaders.from(config.headers).normalize();
  • lib/adapters/fetch.js#L456:发送前执行headers.normalize()再转 ByteString 头部对象
  • lib/core/transformData.js#L22-L25:transformRequest回调执行前后各normalize()一次

也就是说,normalize是 axios 请求管线里保证头部键唯一性的"守门员",而不是可选项。

concat:组合头部并固定大小写预设

concat把当前实例与若干目标组合成新的AxiosHeaders实例:目标是字符串时按原始 HTTP 头格式解析,目标是AxiosHeaders实例时直接合并。这在需要"预置大小写 + 后填值"的组合场景特别有用:

const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } );
concat(...targets: Array<AxiosHeaders | RawAxiosHeaders | string | undefined | null>): AxiosHeaders;

静态实现非常简洁:new this(first)后对每个目标依次set(lib/core/AxiosHeaders.js#L418-L424),因此目标中已有的键默认不会覆盖前者——配合undefined预设即可固定content-type的小写形式。

toJSON 与 toString:序列化的两种形态

toJSON把内部所有头值解析为一个 null-proto 新对象,跳过nullfalse的值;asStringstrue时把数组值用逗号连接为字符串:

toJSON(asStrings: true): Record<string, string>; toJSON(asStrings?: false): Record<string, string | string[]>;

toString则返回不带 CRLF 的原始 HTTP 头文本块,每行一个name: value对(lib/core/AxiosHeaders.js#L395-L399),底层同样基于toJSON

toString(): string;

另外从源码可以看到实例实现了[Symbol.iterator](迭代toJSON()的 entries)和[Symbol.toStringTag] = 'AxiosHeaders',可以直接for...of遍历或用instanceof识别类型。

静态 from:幂等的类型归一

from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders;

若传入的已经是AxiosHeaders实例则原样返回,否则用其构造新实例。源码就一行:return thing instanceof this ? thing : new this(thing);(lib/core/AxiosHeaders.js#L410-L412)。前面提到的三个适配器正是靠AxiosHeaders.from(config.headers).normalize()把配置里可能是普通对象、字符串或AxiosHeaders的头部统一归一。

快捷访问器:setContentType 一族

类上预置了以下快捷方法(每个头部各有 get/set/has 三个):

  • setContentTypegetContentTypehasContentType
  • setContentLengthgetContentLengthhasContentLength
  • setAcceptgetAccepthasAccept
  • setUserAgentgetUserAgenthasUserAgent
  • setContentEncodinggetContentEncodinghasContentEncoding

它们由AxiosHeaders.accessor静态方法批量生成:以Content-TypeContent-LengthAcceptAccept-EncodingUser-AgentAuthorization六个头部为名单调用accessor(lib/core/AxiosHeaders.js#L452-L459),内部通过buildAccessors在原型上用toCamelCase生成如getContentEncoding这样的方法,转调this.get/set/has.call(this, header, ...)(lib/core/AxiosHeaders.js#L182-L196)。

两个源码层面的工程细节:

  • 访问器属性描述符刻意设为 null-proto(__proto__: null),防止被污染的Object.prototype.get在写入途中把数据描述符变成访问器描述符——这是对原型污染攻击面的防御性写法;
  • accessor是公开的静态方法,你也可以为任意自定义头(如X-Trace-Id)动态生成访问器,tests/unit/axiosHeaders.test.js#L599-L628 中有headers.constructor.accessor('foo')的验证用例。

此外源码里还有一个文档未展开的细节:reduceDescriptorsset/get/...等原型方法做了"保留名"热修(lib/core/AxiosHeaders.js#L461-L470),避免把名为setget的头部当作属性赋值时覆盖掉同名方法。

小结:方法选择速查

场景推荐方法关键依据
初始化 / 批量写入构造器、set(对象、字符串、Map)lib/core/AxiosHeaders.js#L198-L257
条件覆盖set(name, value, rewrite)默认不覆盖false占位值
提取参数(如 boundary)get(name, AxiosHeaders.parseParameters)处理引号、转义与原型污染防护
存在性判断hasfalse也算"已定义"
精准删除delete/clear(matcher)clear的 matcher 作用于键名
消除重复键normalize(format?)适配器与 transformData 管线自动调用
组合 + 大小写预设AxiosHeaders.concatfrom固定头部大小写
序列化toJSON(asStrings?)/toStringnull-proto、跳过false
常用头简写getContentType等访问器accessor可扩展自定义头

AxiosHeaders的设计思路可以概括为:对外提供"大小写不敏感、值自动清洗、重复键可归一"的统一头容器,对内通过normalize保证适配器拿到的总是干净的头部集合。理解了set的 rewrite 语义、parseParameters的引号状态机和normalize在请求管线中的自动触发,你就能在拦截器、实例默认值和请求级配置三处安全地操控请求头。

相关文档:headers 总览(大小写保留与拦截器设置)、api-reference、单元测试 tests/unit/axiosHeaders.test.js。

【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询