web-dev-qa-db-ja.com

MVC Core APIへのフォームデータの投稿

AJAXを使用してAPIにデータを投稿したいのですが、問題があります。APIをテストするためにFiddlerを使用しており、名前を投稿するときにJSONを正しく投稿できます/ value urlencoded string応答本文が '{"":["入力が無効です。"]}'の400 Bad Requestを受け取ります。

デバッグウィンドウが表示されます:Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor:Information: Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.SerializableError'.

投稿されるJSONは次のとおりです。

{
    "Name": "Test"
}

投稿されるフォームデータは次のとおりです。

Name=Test

これがコントローラーとアクションです:

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

これはAccountクラスです。

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

モデルのバインディング中に問題があることは明らかなようですが、フォームデータは有効なようです(AJAXを使用してフォームデータも生成し、400も取得します)。

10
Kevin Shaffer

Andrew Lockは、彼の投稿 ASP.NET CoreでJSON POSTをバインドするモデル で、ASP.NET CoreでJSON POSTをバインドするには、[FromBody]属性が次のように引数で指定されます:

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

[ApiController]のASP.NET Core 2.1の導入により、これはもう必要ありません。ここで重要なのは、バインドされる型が「複雑」な場合(この例の場合)、この属性が[FromBody]属性の存在を効果的に推測することです。つまり、上記で示したようにコードを作成したかのようです。

Andrewは彼の投稿で、次のことも述べています。

場合によっては、両方のタイプのデータをアクションにバインドできる必要があります。その場合、2つの異なるデータセットを同じエンドポイントで受信することはできないため、少し立ち往生しています。

ここで、両方のタイプのデータを参照するとき、AndrewはJSON投稿とフォームベースのPOSTの両方を参照しています。彼は、必要な結果を実際に達成する方法を説明し続けています。目的に合わせて彼の例を変更するには、次のようなことをする必要があります。

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

Andrewの例では、[FromBody]は明示的であり、[FromForm]は暗黙的ですが、[ApiController]がデフォルトに与える影響を考えると、上記の変更された例はそれを反転します。


カスタムFromFormを使用してFromBodyIActionConstraintの両方に同じURLを使用できるようにする潜在的なアプローチについては、私の答え here を参照してください。

18
Kirk Larkin

要求タイプが「application/json」に設定されていることを確認してください。コードを再現しましたが、リクエストタイプをapplication/jsonに設定するまで、Postmanを使用してメソッドは呼び出されませんでした。

編集:フィドラーのヘッダーに次を追加すると、フィドラーがメソッドを呼び出すこともできました。

コンテンツタイプ:application/json

2
James

ヘッダーのContent-Type:application/x-www-form-urlencoded)のフォームデータをAPIコントローラーに取得する場合は、アクションメソッドに[FromForm]属性を配置する必要があります。

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

    }

ヘッダーContent-Type:application/jsonのフォームデータをAPIコントローラーに取得する場合は、アクションメソッドに[FromBody]/No属性を次のように配置する必要があります。

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

    }

または

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

    }