无法在 ASP.NET 核心中往返 JSON - 因为我做了一些愚蠢的事情

Unable to round-trip JSON in ASP.NET Core - because I did something stupid

我正在尝试将 ASP.NET/MVC-.NET4.5 Web 应用程序移动到 ASP.NET CORE .NET 5.0,但我无法获得 json serialization/deserialization 上班。

这取决于服务器能够向浏览器发送 json 对象,并让浏览器将它们发回 - 而 serialization/deserialization 根本不起作用。

我已经创建了一个简单的测试应用程序,它失败了,没有多余的复杂性。

我从 dotnet new webapp -o JsonTesting 开始。

我添加了一个 ApiController

我的创业公司:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    [...]
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapDefaultControllerRoute();
    });
}

我的模型(注意 - 我故意使用不一致的大小写):

public class Foo
{
    public int AnInt { get; set; }
    public double aDouble { get; set; }
    public decimal ADecimal { get; set; }
    public string aString { get; set; }
    public DateTime aDateTime { get; set; }
    public DateTimeOffset ADateTimeOffset { get; set; }
}

我的控制器:

[ApiController]
[Area("api")]
[Route("[area]/[controller]")]
public class FooController : ControllerBase
{
    private List<Foo> _foos;
    public FooController()
    {
        _foos = new List<Foo>
        {
            new Foo{
                AnInt = 1,
                aDouble = 1.1,
                ADecimal = 1m,
                aString = "One",
                aDateTime = DateTime.Parse("2020-01-01T01:01:01"),
                ADateTimeOffset = DateTimeOffset.Parse("2020-01-01T01:01:01")
            },
            new Foo{
                AnInt = 2,
                aDouble = 2.2,
                ADecimal = 2m,
                aString = "Two",
                aDateTime = DateTime.Parse("2020-01-02T02:02:02"),
                ADateTimeOffset = DateTimeOffset.Parse("2020-01-02T02:02:02")
            }
        };
    }

    [HttpGet]
    public ActionResult<IEnumerable<Foo>> Get()
    {
        return _foos;
    }

    [HttpPost]
    public ActionResult<bool> Post(Foo foo)
    {
        var match = _foos.SingleOrDefault(f => f.AnInt == foo.AnInt);
        if (match == null)
            return false;
        if (foo.aDouble != match.aDouble)
            return false;
        if (foo.ADecimal != match.ADecimal)
            return false;
        if (foo.aString != match.aString)
            return false;
        if (foo.aDouble != match.aDouble)
            return false;
        if (foo.aDateTime != match.aDateTime)
            return false;
        if (foo.ADateTimeOffset != match.ADateTimeOffset)
            return false;

        return true;
    }
}

Get returns 一个 Foos 列表,一个 Post 发送一个 Foo 并且 returns 如果它在列表中则为真,否则为假。

然后我在Pages/Index.cshtml:

中添加了一个table
<div class="text-center">
    <table>
        <th>
            <td>Initial</td>
            <td>Buttons</td>
        </th>
        <tbody id="thebody"></tbody>
    </table>
</div>

然后一些 Javascript 调用 GET 端点来填充 table,然后在单击其中一个按钮时调用 PUT 端点:

@section Scripts {
<script type="text/javascript">
    debugger;

    $(document).ready(function ()
    {
        var $thebody = $("#thebody");

        $thebody.empty();

        $.ajax({
            url: '/api/Foo',
            type: 'GET',
            dataType: 'json',
            contentType: 'application/json',
            data: null
        }).done(function (data, textStatus, jqXHR)
        {
            data.forEach(element =>
            {
                var $tr = $("<tr />");

                var $td1 = $("<td />");
                $td1.text(JSON.stringify(element));
                $tr.append($td1);

                var $td2 = $("<td />");
                var $btn = $("<input type='button' />");
                $btn.val(element.aString)
                $btn.data('foo', element);
                $td2.append($btn);
                $tr.append($td2);

                var $td3 = $("<td class='out'/>");
                $tr.append($td3);

                $btn.on('click', function (event)
                {
                    var $target = $(event.target);
                    var foo = $target.data('foo');

                    $.ajax({
                        url: '/api/Foo',
                        type: 'POST',
                        dataType: 'json',
                        contentType: 'application/json',
                        data: JSON.stringify({ 'foo': foo })
                    }).done(function (data, textStatus, jqXHR)
                    {
                        if (data)
                        {
                            alert('Matched');
                        } else
                        {
                            alert('Didn\'t match');
                        }
                    }).fail(function (jqXHR, textStatus, errprThrow)
                    {
                        alert(textStatus);
                    });
                });

                $thebody.append($tr);
            });
        }).fail(function (jqXHR, textStatus, errprThrow)
        {
            alert(textStatus);
        });;
    });

</script>
}

在调试器中检查它,看起来 Foo 对象被正确发送到浏览器,并且被正确保存。当我单击其中一个按钮时,我在 Chrome DTools 中看到的内容看起来是正确的:

foo: {
    "anInt": 1,
    "aDouble": 1.1,
    "aDecimal": 1,
    "aString": "One",
    "aDateTime": "2020-01-01T01:01:01",
    "aDateTimeOffset": "2020-01-01T01:01:01-06:00"
}

但是 FooController.Post() 中出现的内容被清零了。 None 个字段已根据浏览器提供的值设置。

然而,当我查看 DevTools 网络选项卡时,我在请求负载中看到的内容看起来是正确的:

{"foo":{"anInt":1,"aDouble":1.1,"aDecimal":1,"aString":"One","aDateTime":"2020-01-01T01:01:01","aDateTimeOffset":"2020-01-01T01:01:01-06:00"}}

关于我做错了什么有什么想法吗?

这在 .NET 4.5 中使用 NewtonSoft.JSON 才行得通。我没想到迁移到 .NET 5.0 会遇到问题。

===

好的,事情就是这样。我主要是后端开发人员,我正在复制我们的前端开发人员常用的模式。或者复制一半。

在 JQuery POST 上,我在 data 字段中输入的内容需要匹配端点函数的参数。当然可以。

我们的前端开发人员通常使用数据传输对象 (DTO) 执行此操作。我在 javascript 中提供了 DTO,但我没有在端点中接受 DTO。

一个解决方法是传递 Foo 对象本身,如 Brando Zhang 的回答中所建议的:

data: JSON.stringify(foo)

并且端点继续接受 Foo:

[HttpPost]
public ActionResult<bool> Post(Foo foo)
{
    [...]
}

另一种是继续传一个DTO:

data: JSON.stringify({ 'foo': foo })

然后更改端点以接受 DTO:

public class FooDto
{
    public Foo Foo { get; set; }
}

[HttpPost]
public ActionResult<bool> Post(FooDto dto)
{
    Foo foo = dto.Foo;

    [...]
}

两者都有效。

您需要使用 FromBodyAttribute:

注释您的 POST 参数
    [HttpPost]
    public ActionResult<bool> Post([FromBody] Foo foo)

据我所知,您的 Json 格式有问题,正确的 Json 格式应该如下所示:

{"anInt":1,"aDouble":1.1,"aDecimal":1,"aString":"One","aDateTime":"2020-01-01T01:01:01","aDateTimeOffset":"2020-01-01T01:01:01-06:00"}

所以你应该像下面这样修改 jquery:

@section Scripts {
<script type="text/javascript">
    debugger;

    $(document).ready(function ()
    {
        var $thebody = $("#thebody");

        $thebody.empty();

        $.ajax({
            url: '/api/Foo',
            type: 'GET',
            dataType: 'json',
            contentType: 'application/json',
            data: null
        }).done(function (data, textStatus, jqXHR)
        {
            data.forEach(element =>
            {
                var $tr = $("<tr />");

                var $td1 = $("<td />");
                $td1.text(JSON.stringify(element));
                $tr.append($td1);

                var $td2 = $("<td />");
                var $btn = $("<input type='button' />");
                $btn.val(element.aString)
                $btn.data('foo', element);
                $td2.append($btn);
                $tr.append($td2);

                var $td3 = $("<td class='out'/>");
                $tr.append($td3);

                $btn.on('click', function (event)
                {
                    var $target = $(event.target);
                    var foo = $target.data('foo');

                    $.ajax({
                        url: '/api/Foo',
                        type: 'POST',
                        dataType: 'json',
                        contentType: 'application/json',
                        data: JSON.stringify( foo )
                    }).done(function (data, textStatus, jqXHR)
                    {
                        if (data)
                        {
                            alert('Matched');
                        } else
                        {
                            alert('Didn\'t match');
                        }
                    }).fail(function (jqXHR, textStatus, errprThrow)
                    {
                        alert(textStatus);
                    });
                });

                $thebody.append($tr);
            });
        }).fail(function (jqXHR, textStatus, errprThrow)
        {
            alert(textStatus);
        });;
    });

</script>
}

结果: