web-dev-qa-db-ja.com

ASP.NET CoreがステータスコードとともにJSONを返す

私は私の.NET Core Web APIコントローラでHTTPステータスコードと共にJSONを返す正しい方法を探しています。私はこのようにそれを使うのに使います:

public IHttpActionResult GetResourceData()
{
    return this.Content(HttpStatusCode.OK, new { response = "Hello"});
}

これは4.6 MVCアプリケーションでしたが、今では.NET Coreで私はこれを持っていないようですIHttpActionResult私はActionResultを持っていて、このように使用します:

public ActionResult IsAuthenticated()
{
    return Ok(Json("123"));
}

しかし、サーバーからの応答は下の画像のように奇妙です。

enter image description here

Web API 2で行ったように、Web APIコントローラーにHTTPステータスコードを付けてJSONを返すようにしたいだけです。

95
Rossco

JsonResultで応答する最も基本的なバージョンは以下のとおりです。

// GET: api/authors
[HttpGet]
public JsonResult Get()
{
    return Json(_authorRepository.List());
}

ただし、自分のレスポンスコードを明示的に処理することはできないため、これは問題の解決には役立ちません。

ステータスの結果を制御する方法は、ActionResultを返す必要があることです。ここでStatusCodeResult型を利用することができます。

例えば:

// GET: api/authors/search?namelike=foo
[HttpGet("Search")]
public IActionResult Search(string namelike)
{
    var result = _authorRepository.GetByNameSubstring(namelike);
    if (!result.Any())
    {
        return NotFound(namelike);
    }
    return Ok(result);
}

上記の例は両方とも、マイクロソフトのドキュメントから入手できる優れたガイドからのものです。 レスポンスデータのフォーマット


余分なもの

私がよく出くわす問題は、VSの "New Project"テンプレートのデフォルト設定をそのまま使うのではなく、自分のWebAPIをよりきめ細かく制御したいということです。

いくつかの基本を理解していることを確認しましょう。

ステップ1:あなたのサービスを設定する

ASP.NET Core WebAPIがステータスコードを完全に制御しながらJSONシリアル化オブジェクトで応答するようにするには、まずStartup.csにあるConfigureServicesメソッドにAddMvc()サービスを含めたことを確認することから始めます。

AddMvc()が他のリクエストタイプへの応答と共にJSON用のInput/Output Formatterを自動的に含むことに注意することは重要です。

プロジェクトが フルコントロール を必要とし、WebAPIがapplication/jsonを含むさまざまな要求タイプに対してどのように動作し、他の要求タイプ(標準的なブラウザ要求など)に応答しないかなど、サービスを厳密に定義する場合次のコードで手動で定義できます。

public void ConfigureServices(IServiceCollection services)
{
    // Build a customized MVC implementation, without using the default AddMvc(), instead use AddMvcCore().
    // https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc/MvcServiceCollectionExtensions.cs

    services
        .AddMvcCore(options =>
        {
            options.RequireHttpsPermanent = true; // does not affect api requests
            options.RespectBrowserAcceptHeader = true; // false by default
            //options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();

            //remove these two below, but added so you know where to place them...
            options.OutputFormatters.Add(new YourCustomOutputFormatter()); 
            options.InputFormatters.Add(new YourCustomInputFormatter());
        })
        //.AddApiExplorer()
        //.AddAuthorization()
        .AddFormatterMappings()
        //.AddCacheTagHelper()
        //.AddDataAnnotations()
        //.AddCors()
        .AddJsonFormatters(); // JSON, or you can build your own custom one (above)
}

他のシリアル化フォーマット(protobuf、thriftなど)に応答したい場合に備えて、私はあなたがあなた自身のカスタム入出力フォーマッターを追加する方法も含んでいることに気付くでしょう。

上記のコードの大部分は、ほとんどがAddMvc()メソッドの複製です。ただし、テンプレートを使用して出荷済みのサービスを使用するのではなく、すべてのサービスを定義することによって、各「デフォルト」サービスを独自に実装しています。私はコードブロックにリポジトリリンクを追加しました、あるいはあなたはGitHubリポジトリからAddMvc()をチェックアウトすることができます。

最初の段階で実装しないのではなく、デフォルトを「元に戻す」ことでこれを解決しようとするガイドがいくつかあることに注意してください。冗長な作業、悪いコード、率直に言ってすぐに消える古い習慣です。


ステップ2:コントローラを作成する

質問を整理するためだけに、本当にわかりやすい方法を紹介します。

public class FooController
{
    [HttpPost]
    public async Task<IActionResult> Create([FromBody] Object item)
    {
        if (item == null) return BadRequest();

        var newItem = new Object(); // create the object to return
        if (newItem != null) return Ok(newItem);

        else return NotFound();
    }
}

ステップ3:Content-TypeAcceptを確認してください

request 内のContent-TypeおよびAcceptヘッダーが正しく設定されていることを確認する必要があります。あなたの場合(JSON)では、それをapplication/jsonに設定します。

WebAPIがデフォルトとしてJSONとして応答するようにしたい場合は、要求ヘッダーの指定内容に関係なく、 の2つの方法で を使用してそれを実行できます。

方法1 先ほどお勧めした記事( レスポンスデータのフォーマット )にあるように、コントローラ/アクションレベルで特定のフォーマットを強制することができます。私は個人的にはこのアプローチが好きではありません...しかしここでそれは完全を期すためのものです:

特定のフォーマットを強制する 可能な特定のアクションに対してレスポンスフォーマットを制限したい場合は、[Produces]フィルタを適用できます。 [Produces]フィルタは、特定のアクション(またはコントローラ)に対する応答フォーマットを指定します。ほとんどのフィルタと同様に、これはアクション、コントローラ、またはグローバルスコープで適用できます。

[Produces("application/json")]
public class AuthorsController

アプリケーション用に他のフォーマッタが設定されていて、クライアントが利用可能な別のフォーマットを要求するAuthorsControllerヘッダを提供している場合でも、[Produces]フィルタはAccept内のすべてのアクションにJSONフォーマットの応答を返させます。

方法2 WebAPIが要求されたフォーマットですべての要求に応答するための推奨方法。ただし、要求されたフォーマットが受け入れられない場合は、 フォールバック をデフォルト(つまりJSON)に戻します。

最初に、あなたはそれをあなたのオプションに登録する必要があります(前述のように、デフォルトの振る舞いを作り直す必要があります)。

options.RespectBrowserAcceptHeader = true; // false by default

最後に、単にサービスビルダーで定義されたフォーマッターのリストを並べ替えることによって、Webホストはデフォルトであなたがリストの一番上に位置するフォーマッター(すなわち位置0)になるでしょう。

より多くの情報はこの中で見つけることができます .NET Web開発とツールブログエントリ

129
Svek

最も一般的なステータスコードに対して事前定義された方法があります。

  • Ok(result)はレスポンスとともに200を返します
  • CreatedAtRoute201 +新しいリソースURLを返します
  • NotFound404を返します
  • BadRequest400などを返します。

すべてのメソッドのリストについては、 BaseController.cs および Controller.cs を参照してください。

しかし、あなたが本当にカスタムコードを設定するためにStatusCodeを使うことができると主張するならば、あなたはそれがコードを読みにくくするので実際にはすべきではなく、ヘッダを設定するためにコードを繰り返す必要があります(CreatedAtRouteのように) ).

public ActionResult IsAuthenticated()
{
    return StatusCode(200, Json("123"));
}
41
Tseng

ASP.NET Core 2.0では、Web API(MVCと統合され、同じ基本クラスControllerを使用)からオブジェクトを返す理想的な方法は次のとおりです。

public IActionResult Get()
{
    return new OkObjectResult(new Item { Id = 123, Name = "Hero" });
}

それに注意してください

  1. 200 OKステータスコードで戻ります(Ok型のObjectResult
  2. コンテンツのネゴシエーションを行います。つまり、リクエストのAcceptヘッダーに基づいて返されます。 Accept: application/xmlがリクエストで送信された場合、それはXMLとして返されます。何も送信されない場合、JSONがデフォルトです。

特定のステータスコード付きを送信する必要がある場合は、代わりにObjectResultまたはStatusCodeを使用してください。どちらも同じことを行い、コンテンツネゴシエーションをサポートします。

return new ObjectResult(new Item { Id = 123, Name = "Hero" }) { StatusCode = 200 };
return StatusCode( 200, new Item { Id = 123, Name = "Hero" });

特にJSONとして返すを使いたい場合は、いくつかの方法があります。

//GET http://example.com/api/test/asjson
[HttpGet("AsJson")]
public JsonResult GetAsJson()
{
    return Json(new Item { Id = 123, Name = "Hero" });
}

//GET http://example.com/api/test/withproduces
[HttpGet("WithProduces")]
[Produces("application/json")]
public Item GetWithProduces()
{
    return new Item { Id = 123, Name = "Hero" };
}

それに注意してください

  1. 両方とも2つの異なる方法でJSONを強制します。
  2. どちらもコンテンツネゴシエーションを無視します。
  3. 最初の方法は特定のシリアライザJson(object)を使ってJSONを強制します。
  4. 2番目の方法は、contentType = application/jsonとともにProduces()属性(これはResultFilterです)を使用して同じことを行います。

それらについての詳細は 公式ドキュメント で読んでください。 フィルタについてはこちら を参照してください。

サンプルで使用されている単純なモデルクラス

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}
25
Arghya C

私が思いついた最も簡単な方法は次のとおりです。

return new JsonResult(result)
{
    StatusCode = 201 // Status code here 
};
14
Gerald Hughes

これが私の最も簡単な解決策です。

public IActionResult InfoTag()
{
    return Ok(new {name = "Fabio", age = 42, gender = "M"});
}

または

public IActionResult InfoTag()
{
    return Json(new {name = "Fabio", age = 42, gender = "M"});
}
6
Fabio

Enumを使用して404/201ステータスコードを使用する代わりに

     public async Task<IActionResult> Login(string email, string password)
    {
        if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
        { 
            return StatusCode((int)HttpStatusCode.BadRequest, Json("email or password is null")); 
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));

        }
        var passwordSignInResult = await _signInManager.PasswordSignInAsync(user, password, isPersistent: true, lockoutOnFailure: false);
        if (!passwordSignInResult.Succeeded)
        {
            return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));
        }
        return StatusCode((int)HttpStatusCode.OK, Json("Sucess !!!"));
    }
2
ram dev

私のAsp Net Core APIアプリケーションで行うことは、ObjectResultから拡張するクラスを作成し、コンテンツとステータスコードをカスタマイズするための多くのコンストラクタを提供することです。それから私の全てのコントローラーのアクションは適切なものとしてコストラクタの一つを使います。あなたは私の実装を見てみることができます: https://github.com/melardev/AspNetCoreApiPaginatedCrud

そして

https://github.com/melardev/ApiAspCoreEcommerce

これがクラスの様子です(フルコードについては私のレポを参照してください)。

public class StatusCodeAndDtoWrapper : ObjectResult
{



    public StatusCodeAndDtoWrapper(AppResponse dto, int statusCode = 200) : base(dto)
    {
        StatusCode = statusCode;
    }

    private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, string message) : base(dto)
    {
        StatusCode = statusCode;
        if (dto.FullMessages == null)
            dto.FullMessages = new List<string>(1);
        dto.FullMessages.Add(message);
    }

    private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, ICollection<string> messages) : base(dto)
    {
        StatusCode = statusCode;
        dto.FullMessages = messages;
    }
}

Dtoをあなたのオブジェクトに置き換えたbase(dto)に注意してください。

0
Melardev

ここで見つけた素晴らしい回答と、このreturnステートメントを試してみましたStatusCode(whatever code you wish)を参照してください。

return Ok(new {
                    Token = new JwtSecurityTokenHandler().WriteToken(token),
                    Expiration = token.ValidTo,
                    username = user.FullName,
                    StatusCode = StatusCode(200)
                });
0
Oge Nwike