Project Icon

ky

基于Fetch API的轻量级现代HTTP客户端

ky是基于Fetch API开发的轻量级HTTP客户端,适用于现代浏览器、Node.js、Bun和Deno环境。它提供简化的API、方法快捷键、错误处理、请求重试、JSON操作和超时支持等功能,相比原生fetch更加便捷。作为一个无依赖的小型包,ky为开发者简化了HTTP请求处理流程。

Ky是一个基于Fetch API的小巧而优雅的HTTP客户端

覆盖率状态

Ky针对现代浏览器、Node.js、Bun和Deno。

它只是一个没有依赖项的小型包。

相比普通fetch的优势

  • 更简单的API
  • 方法快捷方式(ky.post()
  • 将非2xx状态码视为错误(在重定向之后)
  • 重试失败的请求
  • JSON选项
  • 超时支持
  • URL前缀选项
  • 具有自定义默认值的实例
  • 钩子
  • TypeScript便利性(例如,.json()支持泛型并默认为unknown,而不是any

安装

npm install ky
下载
CDN

使用

import ky from 'ky';

const json = await ky.post('https://example.com', {json: {foo: true}}).json();

console.log(json);
//=> `{data: '🦄'}`

使用普通的fetch,它将是:

class HTTPError extends Error {}

const response = await fetch('https://example.com', {
	method: 'POST',
	body: JSON.stringify({foo: true}),
	headers: {
		'content-type': 'application/json'
	}
});

if (!response.ok) {
	throw new HTTPError(`Fetch error: ${response.statusText}`);
}

const json = await response.json();

console.log(json);
//=> `{data: '🦄'}`

如果你使用的是Deno,从URL导入Ky。例如,使用CDN:

import ky from 'https://esm.sh/ky';

API

ky(input, options?)

inputoptionsfetch相同,还提供了额外的options(见下文)。

返回一个带有Body方法Response对象,以方便使用。因此,你可以直接调用ky.get(input).json(),而不必先等待Response。当这样调用时,将根据使用的body方法设置适当的Accept头。与window.FetchBody方法不同,如果响应状态不在200...299范围内,这些方法将抛出HTTPError。此外,如果body为空或响应状态为204.json()将返回空字符串,而不是因为空body而抛出解析错误。

import ky from 'ky';

const user = await ky('/api/user').json();

console.log(user);

⌨️ TypeScript: 接受一个可选的类型参数,默认为unknown,并传递给.json()的返回类型。

import ky from 'ky';

// user1是unknown类型
const user1 = await ky('/api/users/1').json();
// user2是User类型
const user2 = await ky<User>('/api/users/2').json();
// user3是User类型
const user3 = await ky('/api/users/3').json<User>();

console.log([user1, user2, user3]);

ky.get(input, options?)

ky.post(input, options?)

ky.put(input, options?)

ky.patch(input, options?)

ky.head(input, options?)

ky.delete(input, options?)

options.method设置为方法名并发起请求。

⌨️ TypeScript: 接受一个可选的类型参数,用于JSON响应(参见ky())。

input

类型:string | URL | Request

fetch input相同。

当使用Request实例作为input时,任何改变URL的选项(如prefixUrl)将被忽略。

options

类型:object

fetch options相同,外加以下额外选项:

method

类型:string
默认值:'get'

用于发起请求的HTTP方法。

内部会将标准方法(GETPOSTPUTPATCHHEADDELETE)转为大写,以避免由于大小写敏感性导致的服务器错误。

json

类型:objectJSON.stringify()接受的任何其他值

发送JSON的快捷方式。使用此选项代替body选项。接受任何普通对象或值,这些值将被JSON.stringify()处理并在正文中发送,同时设置正确的头部。

searchParams

类型:string | object<string, string | number | boolean> | Array<Array<string | number | boolean>> | URLSearchParams
默认值:''

要包含在请求URL中的搜索参数。设置此项将覆盖输入URL中的所有现有搜索参数。

接受URLSearchParams()支持的任何值。

prefixUrl

类型:string | URL

在发起请求时,要预先添加到input URL的前缀。它可以是任何有效的URL,相对或绝对。末尾的斜杠/是可选的,如果需要,在与input连接时会自动添加。仅当input是字符串时生效。使用此选项时,input参数不能以斜杠/开头。

在使用ky.extend()创建特定领域的Ky实例时非常有用。

import ky from 'ky';
// 在 https://example.com 上

const response = await ky('unicorn', {prefixUrl: '/api'});
//=> 'https://example.com/api/unicorn'

const response2 = await ky('unicorn', {prefixUrl: 'https://cats.com'});
//=> 'https://cats.com/unicorn'

注意:

  • 在连接 prefixUrlinput 后,结果会根据页面的基础 URL(如果有)进行解析。
  • 使用此选项时,不允许 input 中有前导斜杠,以确保一致性并避免对 input URL 处理方式产生混淆。因为使用 prefixUrl 时,input 不会遵循正常的 URL 解析规则,这改变了前导斜杠的含义。
retry

类型:object | number 默认值:

  • limit2
  • methodsget put head delete options trace
  • statusCodes408 413 429 500 502 503 504
  • afterStatusCodes413429503
  • maxRetryAfterundefined
  • backoffLimitundefined
  • delayattemptCount => 0.3 * (2 ** (attemptCount - 1)) * 1000

一个对象,表示最大重试次数、允许的方法、允许的状态码、允许使用 Retry-After 时间的状态码,以及最大 Retry-After 时间的 limitmethodsstatusCodesafterStatusCodesmaxRetryAfter 字段。

如果 retry 是一个数字,它将被用作 limit,其他默认值保持不变。

如果响应提供的 HTTP 状态包含在 afterStatusCodes 中,Ky 将等待 Retry-After 头部给出的日期、超时或时间戳过去后再重试请求。如果缺少 Retry-After,则使用非标准的 RateLimit-Reset 头部作为备用。如果提供的状态码不在列表中,将忽略 Retry-After 头部。

如果 maxRetryAfter 设置为 undefined,它将使用 options.timeout。如果 Retry-After 头部大于 maxRetryAfter,它将使用 maxRetryAfter

backoffLimit 选项是每次重试延迟的上限(以毫秒为单位)。 要限制延迟,可以将 backoffLimit 设置为 1000,例如。 默认情况下,延迟使用 0.3 * (2 ** (attemptCount - 1)) * 1000 计算。延迟呈指数增长。

delay 选项可用于更改计算重试之间延迟的方式。该函数接收一个参数,即尝试次数,从 1 开始。

超时后不会触发重试。

import ky from 'ky';

const json = await ky('https://example.com', {
	retry: {
		limit: 10,
		methods: ['get'],
		statusCodes: [413],
		backoffLimit: 3000
	}
}).json();
timeout

类型:number | false 默认值:10000

获取响应的超时时间(毫秒),包括任何重试。不能大于 2147483647。 如果设置为 false,则不会有超时。

hooks

类型:object<string, Function[]> 默认值:{beforeRequest: [], beforeRetry: [], afterResponse: []}

钩子允许在请求生命周期中进行修改。钩子函数可以是异步的,并且按顺序运行。

hooks.beforeRequest

类型:Function[] 默认值:[]

此钩子使您能够在请求发送之前修改请求。在此之后,Ky 不会对请求做任何进一步的更改。钩子函数接收 requestoptions 作为参数。例如,您可以在这里修改 request.headers

钩子可以返回一个 Request 来替换即将发出的请求,或返回一个 Response 来完全避免发出 HTTP 请求。这可以用于模拟请求、检查内部缓存等。从此钩子返回请求或响应时的一个重要考虑因素是,任何剩余的 beforeRequest 钩子将被跳过,因此您可能只想从最后一个钩子返回它们。

import ky from 'ky';

const api = ky.extend({
	hooks: {
		beforeRequest: [
			request => {
				request.headers.set('X-Requested-With', 'ky');
			}
		]
	}
});

const response = await api.get('https://example.com/api/users');
hooks.beforeRetry

类型:Function[] 默认值:[]

此钩子使您能够在重试之前修改请求。在此之后,Ky 不会对请求做任何进一步的更改。钩子函数接收一个包含规范化的请求和选项、错误实例以及重试次数的对象。例如,您可以在这里修改 request.headers

如果请求收到响应,错误将是 HTTPError 类型,并且 Response 对象将在 error.response 中可用。请注意,某些类型的错误(如网络错误)本质上意味着没有收到响应。在这种情况下,错误将不是 HTTPError 的实例。

您可以通过抛出错误来阻止 Ky 重试请求。Ky 不会以任何方式处理它,错误将传播给请求发起者。在这种情况下,剩余的 beforeRetry 钩子将不会被调用。或者,您可以返回 ky.stop 符号来做同样的事情,但不传播错误(这有一些限制,详见 ky.stop 文档)。

import ky from 'ky';

const response = await ky('https://example.com', {
	hooks: {
		beforeRetry: [
			async ({request, options, error, retryCount}) => {
				const token = await ky('https://example.com/refresh-token');
				request.headers.set('Authorization', `token ${token}`);
			}
		]
	}
});
hooks.beforeError

类型:Function[] 默认值:[]

此钩子使您能够在抛出 HTTPError 之前修改它。钩子函数接收一个 HTTPError 作为参数,并应返回一个 HTTPError 实例。

import ky from 'ky';

await ky('https://example.com', {
	hooks: {
		beforeError: [
			error => {
				const {response} = error;
				if (response && response.body) {
					error.name = 'GitHubError';
					error.message = `${response.body.message} (${response.status})`;
				}

				return error;
			}
		]
	}
});
hooks.afterResponse

类型:Function[] 默认值:[]

此钩子使您能够读取和可选地修改响应。钩子函数接收规范化的请求、选项和响应的克隆作为参数。如果钩子函数的返回值是 Response 的实例,Ky 将使用它作为响应对象。

import ky from 'ky';
const response = await ky('https://example.com', {
	hooks: {
		afterResponse: [
			(_request, _options, response) => {
				// 你可以对响应做一些处理,比如记录日志
				log(response);

				// 或者返回一个新的 Response 实例来覆盖原响应
				return new Response('一个不同的响应', {status: 200});
			},

			// 或者在遇到 403 错误时使用新的 token 重试
			async (request, options, response) => {
				if (response.status === 403) {
					// 获取新的 token
					const token = await ky('https://example.com/token').text();

					// 使用新 token 重试
					request.headers.set('Authorization', `token ${token}`);

					return ky(request);
				}
			}
		]
	}
});
throwHttpErrors

类型: boolean 默认值: true

当响应状态码非 2xx 时(在跟随重定向之后),抛出 HTTPError。如果要对重定向也抛出错误而不是跟随它们,请将 redirect 选项设置为 'manual'

将此选项设置为 false 可能在检查资源可用性并期望出现错误响应时有用。

注意:如果设为 false,错误响应将被视为成功,请求不会重试。

onDownloadProgress

类型: Function

下载进度事件处理函数。

该函数接收 progresschunk 参数:

  • progress 对象包含以下元素:percenttransferredBytestotalBytes。如果无法获取主体大小,totalBytes 将为 0
  • chunk 参数是 Uint8Array 的实例。第一次调用时为空。
import ky from 'ky';

const response = await ky('https://example.com', {
	onDownloadProgress: (progress, chunk) => {
		// 示例输出:
		// `0% - 0 of 1271 bytes`
		// `100% - 1271 of 1271 bytes`
		console.log(`${progress.percent * 100}% - ${progress.transferredBytes} of ${progress.totalBytes} bytes`);
	}
});
parseJson

类型: Function 默认值: JSON.parse()

用户自定义 JSON 解析函数。

使用场景:

  1. 通过 bourne 解析 JSON 以防止原型污染。
  2. 使用 JSON.parse()reviver 选项 解析 JSON。
import ky from 'ky';
import bourne from '@hapijs/bourne';

const json = await ky('https://example.com', {
	parseJson: text => bourne(text)
}).json();
stringifyJson

类型: Function 默认值: JSON.stringify()

用户自定义 JSON 字符串化函数。

使用场景:

  1. 使用自定义 replacer 函数字符串化 JSON。
import ky from 'ky';
import {DateTime} from 'luxon';

const json = await ky('https://example.com', {
	stringifyJson: data => JSON.stringify(data, (key, value) => {
		if (key.endsWith('_at')) {
			return DateTime.fromISO(value).toSeconds();
		}

		return value;
	})
}).json();
fetch

类型: Function 默认值: fetch

用户自定义 fetch 函数。 必须完全兼容 Fetch API 标准。

使用场景:

  1. 使用自定义 fetch 实现,如 isomorphic-unfetch
  2. 使用某些使用服务器端渲染(SSR)的框架提供的 fetch 包装函数。
import ky from 'ky';
import fetch from 'isomorphic-unfetch';

const json = await ky('https://example.com', {fetch}).json();

ky.extend(defaultOptions)

创建一个新的 ky 实例,覆盖一些默认选项。

ky.create() 不同,ky.extend() 会继承其父实例的默认值。

你可以将 headers 作为 Headers 实例或普通对象传递。

你可以通过传递值为 undefined 的 header 来删除 header。 作为字符串传递 undefined 只有在 header 来自 Headers 实例时才会删除该 header。

类似地,你可以通过显式传递 undefined 来删除现有的 hooks 条目。

import ky from 'ky';

const url = 'https://sindresorhus.com';

const original = ky.create({
	headers: {
		rainbow: 'rainbow',
		unicorn: 'unicorn'
	},
	hooks: {
		beforeRequest: [ () => console.log('before 1') ],
		afterResponse: [ () => console.log('after 1') ],
	},
});

const extended = original.extend({
	headers: {
		rainbow: undefined
	},
	hooks: {
		beforeRequest: undefined,
		afterResponse: [ () => console.log('after 2') ],
	}
});

const response = await extended(url).json();
//=> after 1
//=> after 2

console.log('rainbow' in response);
//=> false

console.log('unicorn' in response);
//=> true

你也可以通过向 .extend() 提供函数来引用父实例的默认值。

import ky from 'ky';

const api = ky.create({prefixUrl: 'https://example.com/api'});

const usersApi = api.extend((options) => ({prefixUrl: `${options.prefixUrl}/users`}));

const response = await usersApi.get('123');
//=> 'https://example.com/api/users/123'

const response = await api.get('version');
//=> 'https://example.com/api/version'

ky.create(defaultOptions)

创建一个具有全新默认值的 Ky 实例。

import ky from 'ky';

// 在 https://my-site.com

const api = ky.create({prefixUrl: 'https://example.com/api'});

const response = await api.get('users/123');
//=> 'https://example.com/api/users/123'

const response = await api.get('/status', {prefixUrl: ''});
//=> 'https://my-site.com/status'

defaultOptions

类型: object

ky.stop

一个可以由 beforeRetry 钩子返回以停止重试的 Symbol。这也会短路剩余的 beforeRetry 钩子。

注意:返回此符号会使 Ky 中止并返回 undefined 响应。在访问响应的任何属性之前,请确保检查响应是否存在,或使用可选链。它也与 .json().text() 等主体方法不兼容,因为没有响应可供解析。一般来说,我们建议抛出错误而不是返回此符号,因为这会导致 Ky 中止然后抛出,从而避免这些限制。

ky.stop 的一个有效用例是在进行副作用请求时防止重试,这种情况下返回的数据并不重要。例如,向服务器记录客户端活动。

import ky from 'ky';

const options = {
	hooks: {
		beforeRetry: [
			async ({request, options, error, retryCount}) => {
				const shouldStopRetry = await ky('https://example.com/api');
				if (shouldStopRetry) {
					return ky.stop;
				}
			}
		]
	}
};

// 注意,如果返回 ky.stop,response 将为 `undefined`。
const response = await ky.post('https://example.com', options);

// 不支持使用 `.text()` 或其他主体方法。
const text = await ky('https://example.com', options).text();

HTTPError

暴露用于 instanceof 检查。错误对象具有 response 属性(包含 Response 对象),request 属性(包含 Request 对象),以及 options 属性(包含规范化的选项,这些选项可能是在使用 ky.create() 创建实例时传递给 ky 的,或者是直接在执行请求时传递的)。

如果在发生 HTTPError 时需要读取实际响应,请在响应对象上调用相应的解析器方法。例如:

try {
	await ky('https://example.com').json();
} catch (error) {
	if (error.name === 'HTTPError') {
		const errorJson = await error.response.json();
	}
}

⌨️ TypeScript: 接受一个可选的类型参数,默认为unknown,并传递给error.response.json()的返回类型。

TimeoutError

请求超时时抛出的错误。它有一个request属性,包含Request对象

提示

发送表单数据

在Ky中发送表单数据与fetch相同。只需将FormData实例传递给body选项。Content-Type头部将自动设置为multipart/form-data

import ky from 'ky';

// `multipart/form-data`
const formData = new FormData();
formData.append('food', 'fries');
formData.append('drink', 'icetea');

const response = await ky.post(url, {body: formData});

如果你想以application/x-www-form-urlencoded格式发送数据,你需要使用URLSearchParams对数据进行编码。

import ky from 'ky';

// `application/x-www-form-urlencoded`
const searchParams = new URLSearchParams();
searchParams.set('food', 'fries');
searchParams.set('drink', 'icetea');

const response = await ky.post(url, {body: searchParams});

设置自定义Content-Type

Ky会根据请求体中的数据自动为每个请求设置适当的Content-Type头部。但是,有些API需要自定义的非标准内容类型,如application/x-amz-json-1.1。使用headers选项,你可以手动覆盖内容类型。

import ky from 'ky';

const json = await ky.post('https://example.com', {
	headers: {
		'content-type': 'application/json'
	},
	json: {
		foo: true
	},
}).json();

console.log(json);
//=> `{data: '🦄'}`

取消请求

Fetch(因此也包括Ky)通过AbortController API内置支持请求取消。了解更多。

示例:

import ky from 'ky';

const controller = new AbortController();
const {signal} = controller;

setTimeout(() => {
	controller.abort();
}, 5000);

try {
	console.log(await ky(url, {signal}).text());
} catch (error) {
	if (error.name === 'AbortError') {
		console.log('Fetch aborted');
	} else {
		console.error('Fetch error:', error);
	}
}

常见问题

如何在Node.js中使用?

Node.js 18及更高版本原生支持fetch,所以你可以直接使用这个包。

如何在使用服务器端渲染(SSR)的Web应用(React、Vue.js等)中使用?

与上述相同。

如何测试使用这个库的浏览器库?

要么使用可以在浏览器中运行的测试运行器,如Mocha,要么使用AVAky-universal了解更多。

如何在不使用Webpack等打包工具的情况下使用?

确保你的代码作为JavaScript模块(ESM)运行,例如在HTML文档中使用<script type="module">标签。然后Ky可以直接被该模块导入,无需打包工具或其他工具。

<script type="module">
import ky from 'https://unpkg.com/ky/distribution/index.js';

const json = await ky('https://jsonplaceholder.typicode.com/todos/1').json();

console.log(json.title);
//=> 'delectus aut autem'
</script>

它与got有何不同?

请参阅我的回答这里。Got由与Ky相同的人维护。

它与axios有何不同?

请参阅我的回答这里

它与r2有何不同?

请参阅我在#10中的回答。

ky是什么意思?

这只是我设法获得的一个随机短npm包名。不过,它在日语中确实有意义:

一种可发短信的俚语,KY是空気読めない(kuuki yomenai)的缩写,字面意思是"无法读懂空气"。这个短语用于形容一个人无法理解暗示的含义。

浏览器支持

最新版本的Chrome、Firefox和Safari。

Node.js支持

Node.js 18及更高版本。

相关

维护者

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号