简体   繁体   English

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

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

I'm trying to move a ASP.NET/MVC-.NET4.5 web application to ASP.NET CORE .NET 5.0, and I can't get json serialization/deserialization to work.我正在尝试将 ASP.NET/MVC-.NET4.5 Web 应用程序移动到 ASP.NET CORE .NET 5.0,但我无法让 json 序列化/反序列化工作。

This depends upon the server being able to send json objects to the browser, and having the browser send them back - and the serialization/deserialization simply doesn't work.这取决于服务器能够将 json 对象发送到浏览器,并让浏览器将它们发回 - 并且序列化/反序列化根本不起作用。

I've created a simple test application, and it fails with no extraneous complexities.我创建了一个简单的测试应用程序,它失败了,没有额外的复杂性。

I started with dotnet new webapp -o JsonTesting .我从dotnet new webapp -o JsonTesting

To it I added an ApiController .我添加了一个ApiController

My Startup:我的启动:

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

My model (Note - I'm intentionally using inconsistent casing):我的模型(注意 - 我故意使用不一致的大小写):

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

My controller:我的控制器:

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

The Get returns a list of Foos, a Post sends a Foo and returns true if it's in the list, false if not. Get 返回一个 Foo 列表,一个 Post 发送一个 Foo 并且如果它在列表中则返回 true,否则返回 false。

Then I added a table to Pages/Index.cshtml:然后我在 Pages/Index.cshtml 中添加了一个table

<div class="text-center">
    <table>
        <th>
            <td>Initial</td>
            <td>Buttons</td>
        </th>
        <tbody id="thebody"></tbody>
    </table>
</div>

And then some Javascript to call the GET endpoint to populate the table, and then to call the PUT endpoint when one of the buttons is clicked:然后一些 Javascript 调用 GET 端点来填充表,然后在单击其中一个按钮时调用 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>
}

Walking through this in the debugger it looks like the Foo objects are being properly sent to the browser, and are being properly saved.在调试器中执行此操作,看起来 Foo 对象已正确发送到浏览器,并已正确保存。 When I click one of the buttons what I see in the Chrome DTools looks correct:当我单击其中一个按钮时,我在 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"
}

But what shows up in FooController.Post() is zeroed out.但是 FooController.Post() 中显示的内容已被清零。 None of the fields have been set from the values that were provided by the browser.没有根据浏览器提供的值设置任何字段。

Yet when I look in the DevTools network tab, what I see in the Request Payload looks correct:然而,当我查看 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"}}

Any ideas as to what I'm doing wrong?关于我做错了什么的任何想法?

This just worked, using NewtonSoft.JSON in .NET 4.5.这刚刚奏效,在 .NET 4.5 中使用 NewtonSoft.JSON。 I hadn't expected to have issues moving to .NET 5.0.我没想到迁移到 .NET 5.0 会出现问题。

=== ===

OK, here's the thing.好的,事情是这样的。 I'm a mostly backend developer, and I was copying a pattern our front-end developers commonly use.我主要是后端开发人员,我正在复制我们的前端开发人员常用的模式。 Or half-copied.或者半抄。

On a JQuery POST, what I put in the data field needs to match the parameters of the endpoint function.在 JQuery POST 中,我放入data字段的内容需要匹配端点函数的参数。 Of course.当然。

Our frontend devs routinely do this using data transfer objects (DTOs).我们的前端开发人员通常使用数据传输对象 (DTO) 来执行此操作。 I was supplying a DTO in the javascript, but I was not accepting a DTO in the endpoint.我在 javascript 中提供了一个 DTO,但我没有在端点中接受一个 DTO。

One fix would be to pass the Foo object itself, as suggested in Brando Zhang's answer:一种解决方法是传递 Foo 对象本身,如 Brando Zhang 的回答中所建议的:

data: JSON.stringify(foo)

And for the endpoint to continue to accept a Foo:为了让端点继续接受 Foo:

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

The other would be to continue to pass a DTO:另一种是继续通过 DTO:

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

And then to change the endpoint to accept a DTO:然后更改端点以接受 DTO:

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

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

    [...]
}

Either works.要么有效。

As far as I know, there is something wrong with your Json format, the right Json format should be like below:据我所知,您的 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"}

So you should modify the jquery like below:所以你应该像下面这样修改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>
}

Result:结果:

在此处输入图片说明

You need to annotate your POST parameter with FromBodyAttribute :您需要使用FromBodyAttribute注释您的 POST 参数:

    [HttpPost]
    public ActionResult<bool> Post([FromBody] Foo foo)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM