如何将FormUrlEncodedContent用于复杂数据类型?

时间:2015-10-11 04:27:15

标签: c# .net dotnet-httpclient

我需要与仅接受表单编码有效负载的第三方端点进行交互。端点需要此端点处的复杂数据类型,这意味着类似这样的东西(但是表单编码,而不是JSON):

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

我的Google搜索和端点的文档表明这应该是这样编码的形式:

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

1 个答案:

答案 0 :(得分: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&b": "c&d"}这样的对将被编码为a%26b=c%26d。实际上,您可以将其发送为%61%26%62=%63%26%64。换句话说,URL解码值但遗忘了接收端的URL解码密钥名称是一个错误。所以忘记了URL- 编码键名。有关哪些字符可以在网址中以何种形式使用的讨论是over here

(*)&#34;直接将数据转移为Content-Type: application/json,这比将其压缩到查询字符串更好。