Durum kodlu ASP.NET Core dönüş JSON


153

Benim .NET Core Web API denetleyicisinde bir HTTP durum kodu ile JSON dönmek için doğru yolu arıyorum. Ben böyle kullanmak için kullanın:

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

Bu bu var görünmemektedir 4,6 MVC uygulamasında ama şimdi NET Çekirdek I was IHttpActionResultben ActionResultve böyle kullanıyor:

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

Ancak sunucudan gelen yanıt, aşağıdaki resimde olduğu gibi gariptir:

resim açıklamasını buraya girin

Sadece Web API denetleyicisinin Web API 2'de yaptığım gibi bir HTTP durum kodu ile JSON döndürmesini istiyorum.


1
"Tamam" yöntemleri durum kodu olarak 200 değerini döndürür. Önceden tanımlanmış yöntemler tüm yaygın vakaları kapsar. 201 (+ yeni kaynak konumlu başlık) döndürmek için CreatedAtRouteyöntem vb.
Kullanırsınız

Yanıtlar:


191

A ile yanıt veren en temel sürüm JsonResult:

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

Ancak, kendi yanıt kodunuzla açık bir şekilde ilgilenemeyeceğiniz için bu sorununuzu çözmeyecektir.

Durum sonuçları üzerinde kontrol sahibi olmanın yolu, daha ActionResultsonra StatusCodeResulttürden yararlanabileceğiniz bir yer döndürmeniz gerektiğidir .

Örneğin:

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

Yukarıdaki örneklerin her ikisinin de Microsoft Belgeler'den edinilebilen harika bir kılavuzdan geldiğini unutmayın: Yanıt Verilerini Biçimlendirme


Ekstra Şeyler

Sık karşılaştığım sorun, VS'deki "Yeni Proje" şablonundaki varsayılan yapılandırma ile gitmek yerine WebAPI'm üzerinde daha ayrıntılı bir kontrol istememdi.

Bazı temel bilgileri edindiğinizden emin olalım ...

1. Adım: Hizmetinizi yapılandırın

ASP.NET Core WebAPI'nizin durum kodunun tam denetimi boyunca bir JSON Seri Nesnesi ile yanıt vermesini sağlamak için, AddMvc()hizmeti ConfigureServicesgenellikle bulunan yönteminize dahil ettiğinizden emin olarak başlamalısınız Startup.cs.

AddMvc()Diğer istek türlerine yanıt vermenin yanı sıra JSON için Giriş / Çıkış Biçimlendiriciyi otomatik olarak içereceğini unutmayın .

Projeniz tam kontrol gerektiriyorsa ve WebAPI'nizin application/jsondiğer istek türlerini (standart tarayıcı isteği gibi) içeren ve bunlara yanıt vermeyecek çeşitli istek türlerine nasıl davranacağı gibi hizmetlerinizi kesin olarak tanımlamak istiyorsanız, bunu manuel olarak tanımlayabilirsiniz. aşağıdaki kod:

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

Başka bir serileştirme biçimine (protobuf, tasarruf, vb.) Yanıt vermek isteyebiliyorsanız, kendi özel Giriş / Çıkış biçimlendiricilerinizi eklemenin bir yolunu da eklediğimi fark edeceksiniz.

Yukarıdaki kod dizisi çoğunlukla AddMvc()yöntemin bir kopyasıdır . Bununla birlikte, her "varsayılan" hizmeti, önceden gönderilen şablonla gitmek yerine her bir hizmeti tanımlayarak kendi başımıza uygularız. Kod bloğundaki havuz bağlantısını ekledim veya AddMvc() GitHub havuzundan kontrol edebilirsiniz . .

Unutmayın, sadece ilk etapta uygulamak yerine, varsayılanları "geri alarak" çözmeye çalışacak bazı kılavuzlar vardır ... , kötü kod ve açıkçası yakında kaybolacak eski bir alışkanlık.


2. Adım: Bir Denetleyici Oluşturun

Sorunuzu sıralamak için size gerçekten basit bir soru göstereceğim.

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. Adım: Content-TypeveAccept

İsteğinizdeki başlıklarınızın Content-Typeve Acceptbaşlıklarınızın doğru ayarlandığından emin olmanız gerekir . Sizin durumunuzda (JSON), bunu ayarlamak istersiniz .application/json

WebAPI'nızın varsayılan olarak JSON olarak yanıt vermesini istiyorsanız, istek üstbilgisinin belirttiklerinden bağımsız olarak bunu birkaç şekilde yapabilirsiniz .

Yol 1 Daha önce önerdiğim makalede gösterildiği gibi ( Yanıt Verilerini Biçimlendirme ) belirli bir biçimi Denetleyici / Eylem düzeyinde zorlayabilirsiniz. Şahsen bu yaklaşımı sevmiyorum ... ama burada tamlık için:

Belirli Bir Biçimi Zorlama Belirli bir eylem için yanıt biçimlerini kısıtlamak isterseniz, [Üretir] filtresini uygulayabilirsiniz. [Üretir] filtresi, belirli bir eylem (veya denetleyici) için yanıt biçimlerini belirtir. Çoğu Filtre gibi, bu işlem, denetleyici veya genel kapsamda uygulanabilir.

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

[Produces]Filtre içinde tüm işlemleri zorlar AuthorsControllerdiğer biçemleyicileri uygulama için yapılandırılmış ve istemci temin edilmiş olsa bile, JSON biçimli yanıtları dönmek için Acceptfarklı, mevcut biçimi talep başlık.

Yol 2 Tercih ettiğim yöntem, WebAPI'nin tüm isteklere istenen biçimde yanıt vermesidir. Ancak, istenen biçimi kabul etmemesi durumunda varsayılana geri dönme (örn. JSON)

İlk olarak, seçeneklerinize kaydetmeniz gerekir (daha önce belirtildiği gibi varsayılan davranışı yeniden işlememiz gerekir)

options.RespectBrowserAcceptHeader = true; // false by default

Son olarak, hizmet oluşturucuda tanımlanan biçimlendiricilerin listesini yeniden sıralayarak, web barındırıcısı varsayılan olarak listenin en üstüne yerleştirdiğiniz biçimlendiriciye (yani 0 konumu) ayarlanacaktır.

Daha fazla bilgi bu .NET Web Geliştirme ve Araçlar Blog girişinde bulunabilir


Gösterdiğiniz çaba için çok teşekkür ederim. Cevabınız bana Şerefe IActionResultile uygulama konusunda ilham verdi return Ok(new {response = "123"});!
Rossco

1
@Rossco Sorun değil. Umarım kodun geri kalanı projeniz geliştikçe size rehberlik eder.
Svek

1
Bu konuyu genişletmek için, burada
WebAPI'yi

Ayarda: RespectBrowserAcceptHeader = true; Bunu neden yaptığınızı açıklamıyorsunuz ve bunu yapmak genellikle gereksiz ve yanlış. Tarayıcılar html ister ve bu nedenle biçimlendirici seçimini yine de etkilememelidir (maalesef krom ne yazık ki XML isteyerek yapar). Kısacası, uzak
durmam

@YishaiGalatzer Cevabımın bu kısmının ana teması, istemci ve API mantığı arasındaki varsayılan ara katman yazılımının nasıl yükleneceğini vurgulamaktı. Bence, RespectBrowserAcceptHeadermüşterilerinizin hatalı biçimlendirilmiş istekler göndermediğinden emin olmak istediğinizde, alternatif bir serileştirici veya daha yaygın olarak kullanılırken çok önemlidir. Bu nedenle, "Projeniz tam kontrol gerektiriyorsa ve hizmetinizi kesin olarak tanımlamak istiyorsanız" seçeneğini vurguladım ve bu ifadenin üstündeki vurgulanan blok alıntısına da dikkat edin.
Svek

57

En yaygın durum kodları için önceden tanımlanmış yöntemleriniz vardır.

  • Ok(result)200yanıtla döner
  • CreatedAtRoutedöndürür 201+ yeni kaynak URL'si
  • NotFound İadeler 404
  • BadRequestdöner 400vb.

Görmek BaseController.csController.csTüm yöntemlerin listesi için ve .

Ancak gerçekten StatusCodeözel bir kod ayarlamak için kullanabileceğiniz konusunda ısrar ediyorsanız , ancak kodu daha az okunabilir hale getirdiği için gerçekten yapmamalısınız ve üstbilgileri ayarlamak için kodu tekrarlamanız gerekir (örneğin CreatedAtRoute).

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

1
bu bana aşağıdaki cevabım hakkında bir fikir verdi. Teşekkür ederim
Oge Nwike

Bu kod ASP.NET Core 2.2 için doğru değildir. Sadece bunu denedim ve içine serializes yarattığı yöntemle. Doğrudan "123" dizesini içermez. JSONActionResultJson()
amedina

1
@amedina: Kötülerim, sadece Json(...)ipi çıkar ve ipi geç StatusCode
Tseng

"Tamam (sonuç)" dediğinde - sonuç nedir? Bir JSON biçim dizesi mi yoksa (otomatik olarak JSON dizesine dönüştürülecek bir C # nesnesi mi?)?
değişken

@variable: Her zaman bir POCO / sınıf / nesne. Bir dize döndürmek istiyorsanız, bunun yerine "İçerik" kullanmanız gerekir
Tseng

43

İle ASP.NET Çekirdek 2.0 , nesneyi döndürmek için ideal şekilde Web API(MVC ile birleşik ve aynı temel sınıf kullanır Controller) olduğu

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

Dikkat edin

  1. 200 OKDurum kodu ile döner (bir Oktür ObjectResult)
  2. İçerik anlaşması yapar, yani Acceptistendiğinde başlığa göre geri döner . Eğer Accept: application/xmlistekte gönderilir, o kadar dönersiniz XML. Hiçbir şey gönderilmezse, JSONvarsayılan değerdir.

Göndermenin gerekiyorsa spesifik durum koduyla , kullanım ObjectResultveya StatusCodeyerine. Her ikisi de aynı şeyi yapar ve içerik pazarlığını destekler.

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

ObjectResult ile daha da ince taneli:

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

Özellikle JSON olarak dönmek istiyorsanız , birkaç yol vardır.

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

Dikkat edin

  1. Her ikisi JSONde iki farklı şekilde uygulanır.
  2. Her ikisi de içerik anlaşmasını yok sayar.
  3. İlk yöntem JSON'u belirli bir serileştiriciyle zorlar Json(object).
  4. İkinci yöntem de Produces()(a ResultFilter) özniteliğini kullanarak aynı şeyi yapar.contentType = application/json

Onlar hakkında daha fazla bilgiyi resmi dokümanlarda bulabilirsiniz . Filtreler hakkında bilgi edinin .

Örneklerde kullanılan basit model sınıfı

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

10
Bu iyi bir cevap çünkü soruya odaklanıyor ve kısaca bazı pratikleri açıklıyor.
netfed

33

Geldiğim en kolay yol:

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

return new JsonResult(result)
{
    StatusCode = StatusCodes.Status201Created // Status code here 
};

2
Bence bunun çözümü @tseng'den daha iyi çünkü çözümü Durum Kodları vb. İçin yinelenen alanlar içeriyor
Christian Sauer

2
Yapabileceğiniz bir gelişme, Microsoft.AspNetCore.Http dosyasında tanımlanan StatusCodes'u şu şekilde kullanmaktır: return new JsonResult (new {}) {StatusCode = StatusCodes.Status404NotFound};
Bryan Bedard

2
Bu kabul edilen cevap olmalı. Json'u evrensel olarak kurmanın yolları olsa da, bazen eski uç noktalarla çalışmak zorundayız ve ayarlar farklı olabilir. Bazı eski uç noktaları desteklemeyi durdurana kadar, bu tam kontrole sahip olmanın nihai yoludur
pqsk

Microsoft.AspNetCore.Mvc.JsonResult, bence tam olarak nitelenmiş bir addır. Hiçbir FQN veya "kullanma" cevapları beni deli ediyor. :) Montaj Microsoft.AspNetCore.Mvc.Core, Sürüm = 3.1.0.0, Kültür = nötr, PublicKeyToken = adb9793829ddae60 // C: \ Program Dosyaları \ dotnet \ packs \ Microsoft.AspNetCore.App.Ref \ 3.1.0 \ ref \ netcoreapp3.1 \ Microsoft.AspNetCore.Mvc.Core.dll
granadaCoder

1
Güçlü bir türüm olduğunda bu benim için çalıştı (bu örnekte "ITem sonucu = yeni Öğe" ... Öğe çalışma zamanında bilinen bir tür). Tipin bilinmediği zaman (bu soruya) cevabım bakın. (Ben bir db .. json vardı ve json türü çalışma zamanında bilinmiyordu). Teşekkürler Gerald.
granadaCoder

15

Bu benim en kolay çözümüm:

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

veya

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

4

Numaralandırma kullanarak 404/201 durum kodlarını kullanmak yerine

     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 !!!"));
    }

Enum harika bir fikir!
Bhimbim

2

Müthiş cevaplar burada buldum ve ben de bu iade beyanı bakın StatusCode(whatever code you wish)çalıştı ve çalıştı !!!

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

1
Bunun gibi! İyi öneri!
ticky

0

Lütfen aşağıdaki koda bakın, farklı tip JSON ile birden fazla durum kodunu yönetebilirsiniz

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.");
    }
}

9
Hayır. Bu kötü.
Phillip Copley

0

Asp Net Core Api uygulamalarımda ne yaptığım, ObjectResult'dan uzanan bir sınıf oluşturmak ve içerik ve durum kodunu özelleştirmek için birçok kurucu sağlamaktır. Daha sonra tüm Denetleyici eylemlerim uygun olarak costructor'lardan birini kullanıyor. Uygulamama şu adresten bakabilirsiniz: https://github.com/melardev/AspNetCoreApiPaginatedCrud

ve

https://github.com/melardev/ApiAspCoreEcommerce

sınıf nasıl görünüyor (tam kod için repo gidin):

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

Nesneniz tarafından dto'yu değiştirdiğiniz tabana (dto) dikkat edin ve gitmek için iyi olmalısınız.


0

Bunu iţe aldým. Benim büyük sorun benim json (benim veritabanında ... ve belirli / bilinen bir tür değil) bir dize oldu.

Tamam, sonunda bunu işe aldım.

////[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

Asp.net çekirdek 3.1 üzerinde olur

#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

Buradan ipucu aldım :: https://www.jianshu.com/p/7b3e92c42b61

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.