【问题标题】:How do I use FormUrlEncodedContent for complex data types?如何将 FormUrlEncodedContent 用于复杂数据类型?
【发布时间】:2015-10-11 04:27:14
【问题描述】:

我需要与只接受表单编码负载的第三方端点进行交互。端点需要复杂的数据类型在这个端点,意思是这样的(但形式编码,而不是 JSON):

{
    "foo": "bar",
    "baz": {
        "zip": "zap"
    }
}

我的谷歌搜索和端点的文档表明这应该像这样编码:

foo=bar&baz[zip]=zap

我正在使用HttpClient,并且我想使用FormUrlEncodedContent,但是当我这样做时,它会将我的[] 替换为转义字符。

public class Tests
{
    [Fact]
    public void Test()
    {
        var content = new Dictionary<String, String>
        {
            { "foo", "bar" },
            { "baz[zip]", "zap" }
        };
        var formContent = new FormUrlEncodedContent(content);
        Assert.Equal("foo=bar&baz[zip]=zap", formContent.ReadAsStringAsync().Result);
    }
}

我最终得到的是:

foo=bar&baz%5Bzip%5D=zap

【问题讨论】:

    标签: c# .net dotnet-httpclient


    【解决方案1】:

    您的问题有两个问题。第一个:

    我的谷歌搜索表明这应该是这样的形式编码:

    foo=bar&baz[zip]=zap
    

    没有。没有将多维键值结构转换为单维结构的约定或标准。

    如果你仔细想想,这样的转变很快就会变得非常笨拙。对象语义很多比 URL 编码语义更具表现力。 FWIW,他们甚至无法就如何将纯数组编码为 URL 达成一致,尽管这很容易实现。

    由于没有标准,因此归结为设置服务器和客户端可以接受的约定。 (*):

    • 将您的对象序列化为 JSON。这会给你一个字符串。
    • 将该字符串作为 URL 参数中的值传输。
    • 在接收端执行相反的操作。

    所以在 JS 中你会这样做:

    encodeURIComponent(JSON.stringify({
        "foo": "bar",
        "baz": {
            "zip": "zap"
        }
    }));
    

    给你

    "%7B%22foo%22%3A%22bar%22%2C%22baz%22%3A%7B%22zip%22%3A%22zap%22%7D%7D"
    

    这可以作为 URL 参数安全地传输并轻松处理。

    对于 .NET,您可以从多个序列化选项中进行选择,其中两个是 DataContractJsonSerializer 和 JavaScriptSerializer,discussed over here

    我强烈建议不要为此任务滚动您自己的序列化方案。


    第二个:

    但是当我这样做时,它会将我的 [] 替换为转义字符。

    当然。 URL 编码的键值对中的键遵循与值相同的规则。像{"a&amp;b": "c&amp;d"} 这样的一对将被编码为a%26b=c%26d。事实上,您可以将其发送为%61%26%62=%63%26%64。换句话说,在接收端忘记 URL 解码键名的 URL 解码值是一个错误。忘记 URL-encoding 键名也是如此。关于可以在 URL 中以什么形式使用的字符的讨论是 over here


    (*) 简称“直接以Content-Type: application/json传输数据,这比将其压缩成查询字符串更可取。

    【讨论】:

    • 正如我在问题中提到的,我正在与第三方 api 进行交互,因此我无法选择序列化格式。他们的文档有一个带有-d bar[zip]=zap 的 curl 示例,Google 建议虽然不是标准的,但这是发送复杂对象编码形式的最常见机制(我认为 jquery 就是这样做的)。我同意 api 不应该这样做,但我仍然必须处理它。
    • 答案中提到,没有标准,一切都基于约定。没有 .NET 函数可以为您提供开箱即用的格式。如果您的端点记录了它所期望的约定,您将需要自己编写适当的序列化程序。
    • @Micah 很抱歉这不是您希望听到的,但这并不能改变答案是正确的事实。 :)
    • 添加一个直接回答问题的新答案或重组这个答案,我会接受它。目前,这个答案认为这个问题是无效的,我不同意。我同意你的结论,即我想做的事情是不可能的,但我相信这个问题是有效和合理的。特别是,删除使用替代编码格式的建议,因为我正在与第三方端点交互的问题。理想情况下,请预先说明不可能从 FormUrlEncodedContent 中获取所需的编码以及原因。
    • 是的,我认为这个问题是无效的,因为你是基于错误的假设开始的,即存在某种标准,并且 .NET 中存在实现该标准的函数。我尽力纠正这些假设。我没有说你想要的不合理,我说你必须自己实现。我冒昧地提出了一个明智的选择,我当然不会接受这个建议。发现此问题的其他人使用它的限制可能较少。
    猜你喜欢
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 2021-03-09
    • 1970-01-01
    • 1970-01-01
    • 2013-10-30
    • 1970-01-01
    • 2020-11-04
    相关资源
    最近更新 更多