How do I use FormUrlEncodedContent for complex data types?

十年热恋 提交于 2021-02-10 04:14:33

问题


I need to interact with a third party endpoint that only accepts form encoded payloads. The endpoint wants complex data types at this endpoint, meaning something like this (but form encoded, not JSON):

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

My googling and the docs for the endpoint indicates that this should be form encoded like this:

foo=bar&baz[zip]=zap

I am using HttpClient and I want to use FormUrlEncodedContent but when I do it is replacing my [] with escaped characters.

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);
    }
}

What I end up with instead is:

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

回答1:


There are two issues in your question. Number one:

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

foo=bar&baz[zip]=zap

No. There is no convention or standard that transforms a multi-dimension key-value structure into a single-dimension one.

If you think about it, such a transformation will become very unwieldy very quickly. Object semantics are a lot more expressive than URL-encoding semantics. FWIW, they can't even agree on how to encode plain arrays into URLs, even though that would easily be possible.

Since there is no standard, it comes down to setting up a convention that server and client can live with. The convention that would cause the least headache and the least chance for nasty bugs would be(*):

  • Serialize your object into JSON. This gives you a string.
  • Transfer that string as the value in an URL parameter.
  • Do the reverse on the receiving end.

So in JS you would do:

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

which gives you

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

This can be transferred safely as an URL parameter and processed with minimal effort.

For .NET you can pick from several serialization options, two of them being the DataContractJsonSerializer and the JavaScriptSerializer, discussed over here.

I would strongly recommend against rolling your own serialization scheme for this task.


Number two:

but when I do it is replacing my [] with escaped characters.

Of course. The keys in URL-encoded key-value pairs are subject to the same rules as the values. A pair like {"a&b": "c&d"} would be encoded as a%26b=c%26d. In fact, you could send it as %61%26%62=%63%26%64. In other words, URL-decoding values but forgetting about URL-decoding key names on the receiving end is a bug. So is forgetting about URL-encoding key names. A discussion about what characters can be used in what form in a URL is over here.


(*) Short of "transferring the data as Content-Type: application/json directly, which is preferable to squeezing it into a query string.



来源:https://stackoverflow.com/questions/33061506/how-do-i-use-formurlencodedcontent-for-complex-data-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!