web-dev-qa-db-de.com

Senden von Formulardaten an die MVC Core API

Ich möchte Daten mit AJAX an meine API senden, aber ich habe Probleme. Ich benutze Fiddler, um meine API zu testen, und ich kann JSON korrekt posten, aber wenn ich einen Name/Wert-Code mit einer nichtcodierten Zeichenfolge poste, bekomme ich eine 400 Bad Request mit dem Antworttext '{"": [" . "]} '.

Mein Debug-Fenster wird angezeigt: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor:Information: Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.SerializableError'.

Die veröffentlichte JSON ist:

{
    "Name": "Test"
}

Die gebuchten Formulardaten lauten:

Name=Test

Dies ist der Controller und die Aktion:

[Route("api/[Controller]")]
[ApiController]
public class AccountsController : Controller
{
    [HttpPost]
    public IActionResult CreateAccount(Account account)
    {
        //code
    }
}

Dies ist die Account-Klasse:

public class Account
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Type { get; set; }
    public string Website { get; set; }
}

Es scheint offensichtlich, dass während der Modellbindung ein Problem vorliegt, aber die Formulardaten scheinen gültig zu sein (ich habe auch Formulardaten mit AJAX generiert und auch eine 400 erhalten).

4
Kevin Shaffer

In seinem Beitrag Model Binding JSON POSTs in ASP.NET Core von 2016 erklärt Andrew Lock, dass zum Binden eines JSON POST in ASP.NET Core das [FromBody]-Attribut im angegeben werden muss Argument wie folgt:

[HttpPost]
public IActionResult CreateAccount([FromBody] Account account)
{
    // ...
}

Mit der Einführung von ASP.NET Core 2.1 von [ApiController] ist dies nicht mehr erforderlich. Von Bedeutung ist hier, dass dieses Attribut effektiv auf das Vorhandensein des [FromBody]-Attributs verweist, wenn der gebundene Typ "komplex" ist (was in Ihrem Beispiel der Fall ist). Mit anderen Worten, es ist, als hätten Sie den Code so geschrieben, wie ich es oben gezeigt habe.

In seinem Beitrag erklärt Andrew auch Folgendes:

In einigen Fällen müssen Sie möglicherweise beide Datentypen an eine Aktion binden. In diesem Fall stecken Sie etwas fest, da es nicht möglich ist, dass derselbe Endpunkt zwei verschiedene Datensätze empfängt.

Wenn hier auf beide Datentypen Bezug genommen wird, bezieht sich Andrew sowohl auf einen JSON-Beitrag als auch auf einen formularbasierten POST. Er erklärt weiter, wie er das geforderte Ergebnis tatsächlich erreichen kann. Wenn Sie sein Beispiel für Ihre Zwecke ändern möchten, müssen Sie Folgendes tun:

// Form.
[HttpPost("FromForm")]
public IActionResult CreateAccountFromForm([FromForm] Account account)) =>
    DoSomething(account);

// JSON.
[HttpPost("FromBody")]
public IActionResult CreateAccountFromBody(Account account) =>
    DoSomething(account);

private IActionResult DoSomething(Account account) {
    // ...
}

In Andrews Beispiel ist der [FromBody] explizit und der [FromForm] ist implizit, aber angesichts des Einflusses, den [ApiController] auf die Standardwerte hat, wird das obige modifizierte Beispiel umgekehrt.

8
Kirk Larkin

Stellen Sie sicher, dass Ihr Anfragetyp auf "application/json" eingestellt ist. Ich habe Ihren Code reproduziert, und die Methode wurde nicht mit Postman aufgerufen, bis ich den Anforderungstyp auf application/json eingestellt habe.

Edit: Als ich den Kopfzeilen in Fiddler Folgendes hinzugefügt habe, konnte ich auch Fiddler dazu bringen, meine Methode aufzurufen:

Inhaltstyp: application/json

2
James

Wenn Sie Formulardaten für den headr-Inhaltstyp: application/x-www-form-urlencoded) an Ihren API-Controller erhalten möchten, müssen Sie das Attribut [FromForm] in die Aktionsmethode einfügen

    // POST: api/Create
    [HttpPost]
    public IActionResult CreateAccount([FromForm] Account account)
    {

    }

Wenn Sie Formulardaten für den Header Content-Type: application/json an Ihren API-Controller senden möchten, müssen Sie [FromBody]/No in die Aktionsmethode setzen 

    // POST: api/Create
    [HttpPost]
    public IActionResult CreateAccount([FromBody] Account account)
    {

    }

Oder

    // POST: api/Create
    [HttpPost]
    public IActionResult CreateAccount(Account account)
    {

    }