将 axios 的请求记录打印成 cURL

哈德韦

共 4063字,需浏览 9分钟

 ·

2021-12-18 01:48



正如 JSON 已经成为普遍的对象序列化格式一样,cURL 也应该是 HTTP 请求的普遍序列化格式。JSON 原本是为 JavaScript 使用的,其本名是 JavaScript Object Notation,但是各种语言都喜欢用它了,连 Java 这个强类型语言也总是要和它打交道,哪怕 JSON 里缺少类型信息,以至于在 Java 中会碰到各种不便。


题外话,这种不便纯粹是强类型导致的,并不是说序列化和反序列化有多难。比如,对于 JavaScript 程序员来说,很熟悉 JSON.stringify 和 JSON.parse。对于 Java 来说,只要放松类型要求(统统 Object),很容易实现一个:

package helpers;

import com.google.gson.Gson;import com.google.gson.GsonBuilder;

public class JsonHelper { private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();

public static String stringify(Object anything) { return gson.toJson(anything); }

public static Object parse(String s) { return gson.fromJson(s, Object.class); }}





package helpers;

import org.junit.jupiter.api.Test;

import java.util.Objects;

import static org.junit.jupiter.api.Assertions.*;

class JsonHelperTest { @Test void testStringify() { Object o = new Object(); String res = JsonHelper.stringify(o);

assertEquals("{}", res); }

...


总之,JSON 已经成为了编程语言中的对象的非常受欢迎的序列化形式。


那么,对于 HTTP 请求,像 JSON 一样受欢迎的序列化形式,就应该是 cURL。


关于 cURL,已经在一篇文章中推荐过:《使用 cURL 提高前后端开发连调中的沟通效率》,这里再补充一点。cURL 一旦拷贝出来,可以方便导入到 Postman,并转化成各种其他语言代码导出:





序列化成 cURL,就是方便重放。一般都可以从抓包工具或者开发者工具来以 cURL 复制前端发出的请求,但其实,很多“后端”也是要再次调用另外的后端服务的,这些请求,比较难以抓包(当然可以使用一些像 w2 这样的工具,但是有很强的代码侵入性,并且需要在部署上下点工夫)。对于后端发出的请求,比较自然的方式就是打印日志,但是分散打印,再手动拼装完全没有必要,应该实现在代码里,自动生成 cURL。


对于 Java 服务,如果使用 FeignClient 来发请求,之前写过一篇《将 FeignClient 的请求记录成 cURL 格式》。而对于 node 服务,常见的是使用 axios 来发送 HTTP 请求,同样,也非常推荐将 axios 的请求记录成 cURL 格式。


附上代码:


TypeScript 版:

import { curlirize, Method } from './curlirize'

describe('curlirize', () => { it('curlize axios requests', () => { const config = { method: Method.GET, params: { q: 's' }, headers: {}, url: 'url', baseURL: 'base/', }

const res = curlirize(config) expect(res).toEqual('cURL to replay: curl -X GET "base/url?q=s" ') })})



import type { AxiosRequestConfig } from 'axios'import { pickBy, isPlainObject } from 'lodash'

export const curlirize = (config: AxiosRequestConfig) => { const { headers: rawHeaders, method = 'GET', baseURL = '', url, params: rawParams = {}, data } = config const headers = { ...rawHeaders?.common, ...(isPlainObject(data) ? { 'Content-Type': 'application/json' } : rawHeaders ? rawHeaders[method.toLowerCase()] : {}), ...pickBy(rawHeaders, (_, key) => !Method[key] && key !== 'common'), }

const query = Array.from(Object.entries(rawParams)).reduce((query, [key, value]) => { if (value === undefined) return query

if (typeof value === 'object') { if (Array.isArray(value)) { value.forEach((item) => query.append(key, item)) } else { // undefined behavior } } else { query.append(key, `${value}`) }

return query }, new URLSearchParams())

const serializedHeaders = headers ? Object.keys(headers).map((key) => `--header "${key}: ${headers[key]}"`) : []

const cmd = `cURL to replay: curl -X ${method.toUpperCase()} "${baseURL}${url}${ query ? `?${query.toString()}` : '' }" ${serializedHeaders.join(' ')} `

if (isPlainObject(data)) { return cmd + `--data '${JSON.stringify(data)}'` } else { return cmd }}

export enum Method { 'get' = 'GET', 'GET' = 'GET', 'delete' = 'DELETE', 'DELETE' = 'DELETE', 'head' = 'HEAD', 'HEAD' = 'HEAD', 'options' = 'OPTIONS', 'OPTIONS' = 'OPTIONS', 'post' = 'POST', 'POST' = 'POST', 'put' = 'PUT', 'PUT' = 'PUT', 'patch' = 'PATCH', 'PATCH' = 'PATCH', 'purge' = 'PURGE', 'PURGE' = 'PURGE', 'link' = 'LINK', 'LINK' = 'LINK', 'unlink' = 'UNLINK', 'UNLINK' = 'UNLINK',}


纯 JavaScript 版,可以参考这个(比较仓促,写得略简):https://github.com/Jeff-Tian/serverless-space/blob/00064fa2dbd75a6daf48cb206491a35a3eae2ba1/src/common/curlirize.ts




浏览 44
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报