ASP.NET Core mengembalikan JSON dengan kode status

153

Saya mencari cara yang benar untuk mengembalikan JSON dengan kode status HTTP di pengontrol .NET Core Web API saya. Saya menggunakan untuk menggunakannya seperti ini:

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

Ini adalah dalam aplikasi 4.6 MVC tapi sekarang dengan NET Inti Saya tampaknya tidak memiliki ini IHttpActionResultaku punya ActionResultdan menggunakan seperti ini:

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

Tetapi respon dari server aneh, seperti pada gambar di bawah ini:

masukkan deskripsi gambar di sini

Saya hanya ingin pengontrol Web API mengembalikan JSON dengan kode status HTTP seperti yang saya lakukan di Web API 2.

Rossco
sumber
1
Metode "ok" mengembalikan 200 sebagai kode status. Metode yang telah ditentukan mencakup semua kasus umum. Untuk Kembali 201 (+ tajuk dengan lokasi sumber daya baru), Anda menggunakan CreatedAtRoutemetode dll.
Tseng

Jawaban:

191

Versi paling dasar merespons dengan JsonResult:

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

Namun, ini tidak akan membantu masalah Anda karena Anda tidak dapat secara eksplisit menangani kode respons Anda sendiri.

Cara untuk mendapatkan kontrol atas hasil status, adalah Anda harus mengembalikan ActionResultyang mana Anda dapat mengambil keuntungan dari StatusCodeResultjenis.

sebagai contoh:

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

Perhatikan kedua contoh di atas berasal dari panduan hebat yang tersedia dari Dokumentasi Microsoft: Memformat Data Tanggapan


Barang ekstra

Masalah yang sering saya temui adalah bahwa saya ingin lebih banyak kontrol granular atas WebAPI saya daripada hanya pergi dengan konfigurasi default dari template "Proyek Baru" di VS.

Mari kita pastikan Anda memiliki beberapa dasar-dasarnya ...

Langkah 1: Konfigurasikan Layanan Anda

Untuk mendapatkan ASP.NET Core WebAPI Anda untuk merespons dengan Obyek Serialisasi JSON bersama kontrol penuh dari kode status, Anda harus memulai dengan memastikan bahwa Anda telah memasukkan AddMvc()layanan dalam ConfigureServicesmetode yang biasanya Anda temukan di Startup.cs.

Penting untuk dicatat bahwa AddMvc()akan secara otomatis menyertakan Input / Output Formatter untuk JSON bersama dengan menanggapi jenis permintaan lainnya.

Jika proyek Anda memerlukan kontrol penuh dan Anda ingin mendefinisikan layanan Anda secara ketat, seperti bagaimana WebAPI Anda akan berperilaku terhadap berbagai jenis permintaan termasuk application/jsondan tidak menanggapi jenis permintaan lain (seperti permintaan browser standar), Anda dapat menentukannya secara manual dengan kode berikut:

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

Anda akan melihat bahwa saya juga menyertakan cara bagi Anda untuk menambahkan format Input / Output kustom Anda sendiri, jika Anda ingin menanggapi format serialisasi lain (protobuf, penghematan, dll).

Potongan kode di atas sebagian besar merupakan duplikat dari AddMvc()metode ini. Namun, kami menerapkan masing-masing layanan "default" dengan menetapkan masing-masing dan setiap layanan alih-alih menggunakan yang sudah dikirim sebelumnya dengan templat. Saya telah menambahkan tautan repositori di blok kode, atau Anda dapat memeriksa AddMvc() dari repositori GitHub. .

Perhatikan bahwa ada beberapa panduan yang akan mencoba menyelesaikan ini dengan "membatalkan" defaultnya, daripada tidak mengimplementasikannya sejak awal ... Jika Anda memperhitungkan bahwa kami sekarang bekerja dengan Open Source, ini adalah pekerjaan yang berlebihan , kode buruk dan terus terang kebiasaan lama yang akan segera menghilang.


Langkah 2: Buat Pengendali

Saya akan menunjukkan kepada Anda yang benar-benar mudah hanya untuk mendapatkan pertanyaan Anda diurutkan.

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

Langkah 3: Periksa Content-TypedanAccept

Anda perlu memastikan bahwa Anda Content-Typedan Acceptheader dalam permintaan Anda diatur dengan benar. Dalam kasus Anda (JSON), Anda ingin mengaturnya menjadi application/json.

Jika Anda ingin WebAPI Anda merespons sebagai JSON sebagai default, terlepas dari apa yang ditentukan header permintaan Anda dapat melakukannya dalam beberapa cara .

Cara 1 Seperti yang ditunjukkan pada artikel yang saya rekomendasikan sebelumnya ( Memformat Data Respons ) Anda bisa memaksa format tertentu pada level Controller / Action. Saya pribadi tidak suka pendekatan ini ... tapi ini untuk kelengkapan:

Memaksa Format Tertentu Jika Anda ingin membatasi format respons untuk tindakan spesifik yang Anda bisa, Anda dapat menerapkan filter [Menghasilkan]. Filter [Produces] menetapkan format respons untuk tindakan tertentu (atau pengontrol). Seperti kebanyakan Filter, ini dapat diterapkan pada action, controller, atau lingkup global.

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

The [Produces]filter akan memaksa semua tindakan dalam AuthorsControllermengembalikan tanggapan JSON-diformat, bahkan jika formatters lain dikonfigurasi untuk aplikasi dan klien memberikan Acceptsundulan meminta, format yang berbeda tersedia.

Cara 2 Metode yang saya sukai adalah agar WebAPI menanggapi semua permintaan dengan format yang diminta. Namun, jika tidak menerima format yang diminta, maka kembali ke default (mis. JSON)

Pertama, Anda harus mendaftarkannya di opsi Anda (kami perlu memperbaiki perilaku default, seperti disebutkan sebelumnya)

options.RespectBrowserAcceptHeader = true; // false by default

Akhirnya, dengan hanya memesan ulang daftar formatters yang ditentukan dalam pembuat layanan, host web akan secara default ke formatter Anda posisi di bagian atas daftar (yaitu posisi 0).

Informasi lebih lanjut dapat ditemukan di entri Blog .NET Web Development dan Tools ini

Svek
sumber
Terima kasih banyak atas upaya yang Anda lakukan. Jawaban Anda mengilhami saya untuk menerapkan IActionResultdengan return Ok(new {response = "123"});Ceria!
Rossco
1
@ Rossco Tidak masalah. Semoga sisa kode akan membantu memandu Anda saat proyek Anda berkembang.
Svek
1
Untuk memperluas topik ini, saya membuat panduan tambahan dan lebih lengkap untuk mengimplementasikan WebAPI di sini: stackoverflow.com/q/42365275/3645638
Svek
Pada pengaturan: RespectBrowserAcceptHeader = true; Anda tidak menjelaskan mengapa Anda melakukannya, dan biasanya tidak perlu dan salah melakukannya. Peramban meminta html, dan karenanya mereka seharusnya tidak memengaruhi pemilihan formatter dengan cara apa pun (sayangnya Chrome melakukannya dengan meminta XML). Singkatnya itu adalah sesuatu yang akan saya
hindari
@YishaiGalatzer Tema utama dari bagian jawaban saya adalah untuk menyoroti cara melepaskan beban middleware default antara klien dan logika API. Menurut pendapat saya, RespectBrowserAcceptHeadersangat penting ketika menerapkan penggunaan serializer alternatif atau lebih umum, ketika Anda ingin memastikan bahwa klien Anda tidak mengirimkan permintaan cacat. Oleh karena itu, saya menekankan "Jika proyek Anda membutuhkan kontrol penuh dan Anda ingin secara tegas mendefinisikan layanan Anda" dan perhatikan kutipan blok yang disorot di atas pernyataan itu juga.
Svek
57

Anda memiliki metode yang telah ditentukan sebelumnya untuk kode status paling umum.

  • Ok(result)kembali 200dengan respons
  • CreatedAtRoutemengembalikan 201+ URL sumber daya baru
  • NotFound kembali 404
  • BadRequestkembali 400dll

Lihat BaseController.csdan Controller.csuntuk daftar semua metode.

Tetapi jika Anda benar-benar bersikeras Anda dapat menggunakan StatusCodeuntuk mengatur kode khusus, tetapi Anda benar-benar tidak boleh karena membuat kode kurang dibaca dan Anda harus mengulang kode untuk mengatur header (seperti untuk CreatedAtRoute).

public ActionResult IsAuthenticated()
{
    return StatusCode(200, "123");
}
Tseng
sumber
1
ini memberi saya wawasan untuk tanggapan saya di bawah. Terima kasih
Oge Nwike
Kode ini tidak benar untuk ASP.NET Core 2.2. Aku hanya mencoba dan serializes ke JSONdalam ActionResultyang diciptakan oleh Json()metode. Itu tidak termasuk string "123" secara langsung.
amedina
1
@amedina: Kasihan saya, hapus saja Json(...)dan berikan string ke StatusCode
Tseng
Ketika Anda mengatakan "Oke (hasil)" - apa hasilnya? Apakah ini format string JSON atau objek C # (yang secara otomatis dikonversi ke string JSON?)?
variabel
@variable: Selalu POCO / class / objek. Jika Anda ingin mengembalikan string, Anda harus menggunakan "Konten"
Tseng
43

Dengan ASP.NET Core 2.0 , cara ideal untuk mengembalikan objek dari Web API(yang disatukan dengan MVC dan menggunakan kelas dasar yang sama Controller) adalah

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

Perhatikan itu

  1. Itu kembali dengan 200 OKkode status (ini adalah Okjenis ObjectResult)
  2. Itu negosiasi konten, yaitu akan kembali berdasarkan Acceptheader dalam permintaan. Jika Accept: application/xmldikirim dalam permintaan, itu akan kembali sebagai XML. Jika tidak ada yang dikirim, JSONdefault.

Jika perlu mengirim dengan kode status tertentu , gunakan ObjectResultatau StatusCodesebagai gantinya. Keduanya melakukan hal yang sama, dan mendukung negosiasi konten.

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

atau bahkan lebih berbutir halus dengan ObjectResult:

 Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection myContentTypes = new Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection { System.Net.Mime.MediaTypeNames.Application.Json };
 String hardCodedJson = "{\"Id\":\"123\",\"DateOfRegistration\":\"2012-10-21T00:00:00+05:30\",\"Status\":0}";
 return new ObjectResult(hardCodedJson) { StatusCode = 200, ContentTypes = myContentTypes };

Jika Anda secara khusus ingin kembali sebagai JSON , ada beberapa cara

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

Perhatikan itu

  1. Keduanya berlaku JSONdalam dua cara yang berbeda.
  2. Keduanya mengabaikan negosiasi konten.
  3. Metode pertama menegakkan JSON dengan serializer tertentu Json(object).
  4. Metode kedua melakukan hal yang sama dengan menggunakan Produces()atribut (yang a ResultFilter) dengancontentType = application/json

Baca lebih lanjut tentang mereka di dokumen resmi . Pelajari tentang filter di sini .

Kelas model sederhana yang digunakan dalam sampel

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}
Arghya C
sumber
10
Ini adalah jawaban yang bagus karena berfokus pada pertanyaan dan menjelaskan beberapa kepraktisan secara singkat.
netfed
33

Cara termudah yang saya lakukan adalah:

var result = new Item { Id = 123, Name = "Hero" };

return new JsonResult(result)
{
    StatusCode = StatusCodes.Status201Created // Status code here 
};
Gerald Hughes
sumber
2
Saya pikir ini lebih baik daripada jawaban dari @teng karena solusinya termasuk bidang digandakan untuk Kode Status dll.
Christian Sauer
2
Satu peningkatan yang dapat Anda lakukan adalah dengan menggunakan StatusCodes yang didefinisikan dalam Microsoft.AspNetCore.Http seperti ini: kembalikan JsonResult baru (new {}) {StatusCode = StatusCodes.Status404NotFound};
Bryan Bedard
2
Ini harus menjadi jawaban yang diterima. Meskipun ada cara untuk mengatur json secara universal, terkadang kita harus bekerja dengan titik akhir yang lama dan pengaturannya bisa berbeda. Sampai kita dapat berhenti mendukung beberapa titik akhir warisan, ini adalah cara terbaik untuk memiliki kontrol penuh
pqsk
Microsoft.AspNetCore.Mvc.JsonResult adalah nama yang sepenuhnya memenuhi syarat saya pikir. Tidak ada jawaban FQN atau "menggunakan" yang membuat saya gila. :) Perakitan Microsoft.AspNetCore.Mvc.Core, Versi = 3.1.0.0, Culture = netral, PublicKeyToken = adb9793829ddae60 // C: \ Program Files \ dotnet \ paket \ Microsoft.AspNetCore.App.Ref \ 3.1.0 \ ref \ netcoreapp3.1 \ Microsoft.AspNetCore.Mvc.Core.dll
granadaCoder
1
Ini bekerja untuk saya ketika saya memiliki tipe yang kuat ("ITem result = new Item" dalam contoh ini ... Item adalah tipe yang dikenal saat runtime)). Lihat jawaban saya (untuk pertanyaan ini) untuk mengetahui kapan tipe ini ~ tidak ~ diketahui. (Saya punya json di db..dan tipe json tidak dikenal saat runtime). Terima kasih Gerald.
granadaCoder
15

Ini solusi termudah saya:

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

atau

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

Alih-alih menggunakan 404/201 kode status menggunakan enum

     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 !!!"));
    }
ram dev
sumber
Enum adalah ide bagus!
Bhimbim
2

Jawaban luar biasa yang saya temukan di sini dan saya juga mencoba pernyataan pengembalian ini StatusCode(whatever code you wish)dan berhasil !!!

return Ok(new {
                    Token = new JwtSecurityTokenHandler().WriteToken(token),
                    Expiration = token.ValidTo,
                    username = user.FullName,
                    StatusCode = StatusCode(200)
                });
Oge Nwike
sumber
1
Seperti yang ini! Saran yang bagus!
centang
0

Silakan merujuk kode di bawah ini, Anda dapat mengelola beberapa kode status dengan tipe JSON yang berbeda

public async Task<HttpResponseMessage> GetAsync()
{
    try
    {
        using (var entities = new DbEntities())
        {
            var resourceModelList = entities.Resources.Select(r=> new ResourceModel{Build Your Resource Model}).ToList();

            if (resourceModelList.Count == 0)
            {
                return this.Request.CreateResponse<string>(HttpStatusCode.NotFound, "No resources found.");
            }

            return this.Request.CreateResponse<List<ResourceModel>>(HttpStatusCode.OK, resourceModelList, "application/json");
        }
    }
    catch (Exception ex)
    {
        return this.Request.CreateResponse<string>(HttpStatusCode.InternalServerError, "Something went wrong.");
    }
}
Suyog
sumber
9
Tidak, ini buruk.
Phillip Copley
0

Apa yang saya lakukan di aplikasi Asp Net Core Api saya adalah membuat kelas yang memanjang dari ObjectResult dan menyediakan banyak konstruktor untuk menyesuaikan konten dan kode status. Maka semua tindakan Controller saya menggunakan salah satu costructors sebagai tepat. Anda dapat melihat implementasi saya di: https://github.com/melardev/AspNetCoreApiPaginatedCrud

dan

https://github.com/melardev/ApiAspCoreEcommerce

di sini adalah bagaimana kelasnya terlihat (buka repo saya untuk kode lengkap):

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

Perhatikan basis (dto) yang Anda gantikan dto oleh objek Anda dan Anda harus baik-baik saja.

Melardev
sumber
0

Saya mendapatkan ini untuk bekerja. Masalah besar saya adalah json saya adalah string (dalam database saya ... dan bukan tipe tertentu / dikenal).

Ok, akhirnya saya berhasil.

////[Route("api/[controller]")]
////[ApiController]
////public class MyController: Microsoft.AspNetCore.Mvc.ControllerBase
////{
                    //// public IActionResult MyMethod(string myParam) {

                    string hardCodedJson = "{}";
                    int hardCodedStatusCode = 200;

                    Newtonsoft.Json.Linq.JObject job = Newtonsoft.Json.Linq.JObject.Parse(hardCodedJson);
                    /* "this" comes from your class being a subclass of Microsoft.AspNetCore.Mvc.ControllerBase */
                    Microsoft.AspNetCore.Mvc.ContentResult contRes = this.Content(job.ToString());
                    contRes.StatusCode = hardCodedStatusCode;

                    return contRes;

                    //// } ////end MyMethod
              //// } ////end class

Saya kebetulan berada di asp.net core 3.1

#region Assembly Microsoft.AspNetCore.Mvc.Core, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
//C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.1.0\ref\netcoreapp3.1\Microsoft.AspNetCore.Mvc.Core.dll

Saya mendapat petunjuk dari sini :: https://www.jianshu.com/p/7b3e92c42b61

granadaCoder
sumber