如何使用FormUrlEncodedContent处理复杂数据类型?

3

我需要与一个只接受表单编码负载的第三方端点进行交互。该端点希望在此端点使用复杂数据类型,即像这样(但是表单编码,而不是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
1个回答

1

您的问题涉及两个问题。第一:

My googling indicates that this should be form encoded like this:

foo=bar&baz[zip]=zap

不,没有任何公约或标准可以将多维键值结构转换为单维结构。

如果你想一下,这样的转换会很快变得非常难以处理。对象语义比URL编码语义更加表达丰富。顺便说一句,他们甚至不能就如何将普通数组编码为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,在此处讨论
我强烈建议不要尝试自己编写序列化方案来完成此任务。

第二点:

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

当然。URL编码的键值对中的键和值遵循相同的规则。例如 {"a&b": "c&d"} 这样的一对将被编码为 a%26b=c%26d。实际上,你可以发送它作为 %61%26%62=%63%26%64。换句话说,在接收端解码值但忘记解码键名是一个 bug。忘记 URL 编码键名也是如此。关于 URL 中哪些字符可以以什么形式使用的讨论在 这里


(*) 除了直接将数据作为Content-Type: application/json传输,这种方式比将数据压缩到查询字符串中更可取。


正如我在问题中提到的,我正在与第三方API进行交互,因此我无法选择序列化格式。他们的文档有一个带有-d bar[zip]=zap的curl示例,而Google建议虽然不是标准,但这是发送复杂对象表单编码的最常见机制(我认为jquery就是这样做的)。我同意API不应该这样做,但我仍然必须处理它。 - Micah Zoltu
如答案所述,没有标准,一切都基于约定。没有.NET函数会直接给你那种格式。如果您的端点记录了它所期望的约定,则需要自己编写适当的序列化程序。 - Tomalak
@Micah,很抱歉你不喜欢听到这个答案,但这并不能改变它是正确的事实。 :) - Tomalak
在我提问之前做研究时,我发现很多答案与您的类似,建议更改序列化格式。在我的搜索中,如果我遇到这个答案并读了前几句话,然后跳过它,因为它开头说的和其他所有人都一样,“更改格式”。如果它是这样开头的:“您无法使用FormUrlEncodedContent进行jQuery样式的复杂对象序列化”,并且最后建议更改格式,我可能会停止搜索并开始自己动手(不幸的是,这就是我不得不做的)。 - Micah Zoltu
你还是误解了我的意思。我没有说这不可能,也没有说你必须更改格式。我说的是没有标准,你必须自己实现一个约定。我已经无法再用其他方式表达了。我开始怀疑你跳过了阅读我的答案,因为你不喜欢第一句话,直接开始反驳它了。而且看起来你也跳过了$.param()的文档,因为它包含两个黄色警告,基本上说的是我在我的答案中说的相同的事情。 - Tomalak
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接