web-dev-qa-db-fra.com

Enregistrement de données de formulaire dans l'API MVC Core

J'aimerais publier des données sur mon API à l'aide de AJAX, mais j'ai des problèmes. J'utilise Fiddler pour tester mon API et je peux publier correctement le code JSON, mais lors de la publication d'une chaîne de nom/valeur avec un code urlencodé, je reçois une requête 400 Bad dont le corps de la réponse est '{"": ["L'entrée n'était pas valide . "]} '.

Ma fenêtre de débogage affiche: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor:Information: Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.SerializableError'.

Le JSON affiché est:

{
    "Name": "Test"
}

Les données de formulaire en cours d’enregistrement sont:

Name=Test

C'est le contrôleur et l'action:

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

Ceci est la classe Account:

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

Il semble évident qu'il y a un problème lors de la liaison du modèle, mais les données de formulaire semblent valides (j'ai également généré des données de formulaire à l'aide de AJAX et j'ai également un nombre 400).

4
Kevin Shaffer

Dans son article Modèle liant les POSTS JSON dans ASP.NET Core de 2016, Andrew Lock explique que pour lier un JSON POST dans ASP.NET Core, l'attribut [FromBody] doit être spécifié sur l'argument, ainsi:

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

Avec l'introduction de [ApiController] dans ASP.NET Core 2.1, cela n'est plus nécessaire. Il est important ici que cet attribut déduise effectivement la présence de l'attribut [FromBody] lorsque le type lié est «complexe» (ce qui est le cas dans votre exemple). En d'autres termes, c'est comme si vous aviez écrit le code comme je l'ai montré ci-dessus.

Dans son message, Andrew a également déclaré ce qui suit:

Dans certains cas, vous aurez peut-être besoin de pouvoir lier les deux types de données à une action. Dans ce cas, vous êtes un peu coincé, car il ne sera pas possible qu'un même point final reçoive deux ensembles de données différents.

Ici, en faisant référence à les deux types de données, Andrew fait référence à la fois à une publication JSON et à une publication POST basée sur un formulaire. Il continue en expliquant comment atteindre le résultat souhaité. En modifiant son exemple pour vos besoins, vous devez procéder comme suit:

// 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) {
    // ...
}

Dans l'exemple d'Andrew, le [FromBody] est explicite et le [FromForm] est implicite, mais compte tenu de l'effet que [ApiController] a sur les valeurs par défaut, l'exemple modifié ci-dessus renverse le problème.

8
Kirk Larkin

Assurez-vous que votre type de demande est défini sur "application/json". J'ai reproduit votre code, et la méthode n'était pas appelée à l'aide de Postman jusqu'à ce que je définisse le type de requête sur application/json.

Edit: Lorsque j’ajoutais le texte suivant aux en-têtes de fiddler, j’ai pu que fiddler appelle également ma méthode:

Type de contenu: application/json

2
James

Si vous voulez obtenir des données de formulaire pour le headr Content-Type: application/x-www-form-urlencoded) sur votre contrôleur api, vous devez alors mettre l'attribut [FromForm] dans la méthode d'action comme

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

    }

Si vous souhaitez obtenir des données de formulaire pour l'en-tête Content-Type: application/json sur votre contrôleur d'api, vous devez insérer [FromBody]/No attribut dans la méthode d'action, comme 

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

    }

Ou

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

    }
1