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

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

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

前情提要

使用 cURL 提高前后端开发连调中的沟通效率 - Jeff Tian的文章 - 知乎

将 FeignClient 的请求记录成 cURL 格式 - Jeff Tian的文章 - 知乎

使用 jmeter 做压测,如何提供 cURL - Jeff Tian的文章 - 知乎

将 axios 的请求记录打印成 cURL - Jeff Tian的文章 - 知乎

以上文章分别介绍了 NodeJs、Java 中如何将 HTTP 请求打印成 cURL 形式,今天再来聊一下在 C# 语言中,如何做同样的事情。

在 C# 中,往往是这样发送 HTTP 请求的:

csharp var request = new HttpRequestMessage(...); var response = await _client.SendAsync(request, cancellationToken);

在前面的 NodeJs 以及 Java 的文章里,我们都是用了请求拦截的方式,来获取到当前请求的配置,将将其序列化成 cURL。不过,在 C# 中,以上的代码示例展示了,其请求对象是在请求之前创建的,所以可以不用拦截请求,直接将请求对象进行序列化。

其实我本人觉得将请求对象序列化成 cURL,应该是一个内置对象。然而但是,并不存在这样的内置方法。

好在 C# 提供了一种扩展对象的机制,通过自定义一个扩展方法,最终在使用时,看起来就像是在调用一个内置方法。这个机制特别棒,不知道在 Java 中有没有类似的机制?

增加扩展方法

代码提交在 https://github.com/Jeff-Tian/leg-godt/commit/9044c5b36cb02b20ef03c44ef5b1bc5aa5d730e7 ,非常简单直接:

csharp using System.Text;

namespace Web; public static class HttpRequestMessageExtensions { public async static Task ToCurlCommand(this HttpRequestMessage request) { var command = new StringBuilder(); command.Append(curl ); command.Append($-X {request.Method.Method} ); command.Append(${request.RequestUri} );

    foreach (var header in request.Headers)
    {
        command.Append($-H {header.Key}: {string.Join(, , header.Value)} );
    }

    if (request.Content is not null)
    {
        var contentType = request.Content.Headers.ContentType?.MediaType;
        if (contentType is not null)
        {
            command.Append($-H Content-Type: {contentType} );
        }

        var content = await request.Content.ReadAsStringAsync();

        command.Append($-d {content} );
    }

    return command.ToString();
}

}

使用效果

代码见 https://github.com/Jeff-Tian/leg-godt/commit/4fdea567b2045e9cb60bf07741b1b3da6b023497 ,主要是:

csharp public async Task<OneOf<Success, Error>> Handle(StrapiEntry body, CancellationToken cancellationToken) { _logger.LogInformation(Received Strapi webhook request to {Event} with {Model}: {FullName}, body.Event, body.Model, body.Entry.Full_name); var yuqueToken = _configuration[YuQue:Token]; Debug.Assert(yuqueToken is not null); var request = new HttpRequestMessage(HttpMethod.Post, https://api.yuque.com/v2/repos/tian-jie/docs); request.Headers.Authorization = new AuthenticationHeaderValue(Bearer, yuqueToken); request.Content = JsonContent.Create(new Dictionary<string, object> { { title, body.Entry.Full_name }, { body, $Created at {body.Entry.CreatedAt} }, { format, markdown }, { public, 0 }, });

    _logger.LogInformation(Sending Yuque request:n{CurlCommand}, await request.ToCurlCommand());

    var response = await _client.SendAsync(request, cancellationToken);

    if (response.IsSuccessStatusCode)

打印出来就是:

WX 20240613 190942 2x 3ac056dcac

彩蛋

以上示例是故意尝试调用语雀 API,但却是以一种错误的方式,所以得到了一个 Forbidden 的结果。要正常调用语雀 API,可以参考其官方文档:https://www.yuque.com/yuque/developer/openapi

不过,官方文档上虽然上传了一个 Swagger API 文档,但是还得自己下载后用工具查看,非常不方便。我这里将它上传到了 Swagger Hub,以供大家直接使用: https://app.swaggerhub.com/apis/Jeff-Tian/yuque-open_api/2.0.1

1718094501839 f5c0a37f a5da 4e4f a823 08b6d47f1ef3 a2a3afde8b

当然,要在线上调通的话,需要执行请求之前,将个人 Token 填好:

1718094528467 c2f305bc a7c0 4405 b694 ba5122f7ca31 91438f638d

希望帮到你节约了几分钟的时间!