ASP.NET Core возвращает JSON с кодом состояния

Я ищу правильный способ вернуть JSON с кодом состояния HTTP в моем контроллере .NET Core Web API. Я использую это так:

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

Но ответ с сервера странный, как на рисунке ниже:

Я просто хочу, чтобы контроллер веб-API возвращал JSON с кодом состояния HTTP, как я делал в Web API 2.

Комментарии к вопросу (1)
Решение

Самая базовая версия, отвечающая 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);
}

Обратите внимание, что оба из этих приведенных выше примеров взяты из отличного руководства, доступного в документации Microsoft: Формирование данных ответов


Дополнительный материал

Проблема, с которой я сталкиваюсь довольно часто, заключается в том, что я хотел получить более детальный контроль над своим WebAPI, а не просто использовать конфигурацию по умолчанию из шаблона «Новый проект» в VS

Давайте удостоверимся, что у вас есть некоторые основы...

Шаг 1: Настройте свой Сервис

Чтобы ваш ASP.NET Core WebAPI отвечал сериализованным объектом JSON вместе с полным контролем кода состояния, вам следует начать с того, чтобы убедиться, что вы включили службу AddMvc () в свой метод ConfigureServices обычно находится в Startup.cs.

Важно отметить, что AddMvc () автоматически включает в себя форматер ввода / вывода для JSON вместе с ответом на другие типы запросов.

Если ваш проект требует полного контроля и вы хотите строго определить свои услуги, например, как ваш 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();

            //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, бережливость и т. Д.).

Часть кода выше в основном является дубликатом метода AddMvc (). Тем не менее, мы реализуем каждую услугу «по умолчанию» самостоятельно, определяя каждую услугу вместо того, чтобы идти с предварительно поставленной с шаблоном. Я добавил ссылку на репозиторий в блок кода, или вы можете проверить AddMvc () из репозитория GitHub..

  • Обратите внимание, что есть некоторые руководства, которые попытаются решить эту проблему, «отключив» настройки по умолчанию, а не просто не реализуя их в первую очередь... Если учесть, что мы сейчас работаем с Open Source, это избыточная работа, плохой код и, честно говоря, старая привычка, которая скоро исчезнет.*

Шаг 2: Создайте контроллер

Я собираюсь показать вам действительно простой, чтобы разобраться в вашем вопросе.

public class FooController
{
    [HttpPost]
    public async Task 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-Type и Accept

Вам необходимо убедиться, что ваши заголовки Content-Type и Accept в вашем request установлены правильно. В вашем случае (JSON) вы захотите настроить его как application / json.

Если вы хотите, чтобы ваш WebAPI отвечал как JSON по умолчанию, независимо от того, что указывает заголовок запроса, вы можете сделать это парами .

Путь 1 Как показано в статье, которую я рекомендовал ранее (Формирование данных ответа), вы можете форсировать определенный формат на уровне контроллера / действия. Мне лично не нравится такой подход... но здесь это для полноты:

Настройка определенного формата Если вы хотите ограничить форматы ответов для определенного действия, которое вы можете, вы можете применить [Производит] фильтр. Фильтр [Производит] указывает ответ форматы для определенного действия (или контроллера). Как и большинство фильтров, это может применяться в действии, контроллере или глобальной области действия.

[Продукты («приложение / json»)] публичный класс AuthorsController

Фильтр [Produces] будет форсировать все действия внутри AutorsController возвращает ответы в формате JSON, даже если они другие форматировщики были настроены для приложения и клиента заголовок Accept, запрашивающий другой доступный формат.

Путь 2 Мой предпочтительный метод заключается в том, чтобы WebAPI отвечал на все запросы с запрошенным форматом. Однако в случае, если он не принимает запрошенный формат, отступает к умолчанию (т.е. JSON)

Во-первых, вам нужно будет зарегистрировать это в ваших опциях (нам нужно переработать поведение по умолчанию, как отмечалось ранее)

options.RespectBrowserAcceptHeader = true; // false by default

Наконец, просто переупорядочив список форматировщиков, которые были определены в построителе услуг, веб-хост по умолчанию будет соответствовать форматеру, который вы позиционируете в верхней части списка (т.е. позиция 0).

Более подробную информацию можно найти в этой записи .NET Web Development and Tools Blog

Комментарии (8)

У вас есть предопределенные методы для большинства распространенных кодов состояния.

  • Ok (результат) возвращает 200 с ответом
  • CreatedAtRoute возвращает 201 + новый URL ресурса
  • NotFound возвращает 404
  • BadRequest возвращает 400 и т. Д.

См. BaseController.cs и Controller.cs для списка всех методов.

Но если вы действительно настаиваете, что можете использовать StatusCode для установки пользовательского кода, но вы действительно не должны этого делать, поскольку это делает код менее читабельным, и вам придется повторять код для установки заголовков (как для CreatedAtRoute).

public ActionResult IsAuthenticated()
{
    return StatusCode(200, "123");
}
Комментарии (3)

С 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. Оба применяют «JSON» двумя разными способами.
  2. Оба игнорируют переговоры по контенту.
  3. Первый метод обеспечивает JSON специальным сериализатором Json (объект).
  4. Второй метод делает то же самое, используя атрибут Produces () (который является ResultFilter) с contentType = application / json

Узнайте больше о них в официальных документах. Узнайте о фильтры здесь.

Простой модельный класс, который используется в образцах

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}
Комментарии (1)

Самый простой способ, которым я придумал, это:

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

return new JsonResult(result)
{
    StatusCode = StatusCodes.Status201Created // Status code here 
};
Комментарии (3)

Это мое самое простое решение:

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

или

public IActionResult InfoTag()
{
    return Json(new {name = "Fabio", age = 42, gender = "M"});
}
Комментарии (0)

Вместо использования кодов состояния 404/201 с использованием enum

     public async Task 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 !!!"));
    }
Комментарии (1)

Потрясающие ответы, которые я нашел здесь, и я также попробовал это ответное утверждение, см. «StatusCode (какой бы код вы ни пожелали)», и оно сработало!!!

return Ok(new {
                    Token = new JwtSecurityTokenHandler().WriteToken(token),
                    Expiration = token.ValidTo,
                    username = user.FullName,
                    StatusCode = StatusCode(200)
                });
Комментарии (0)

Что я делаю в своих приложениях 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(1);
        dto.FullMessages.Add(message);
    }

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

Обратите внимание на базу (dto), которую вы заменяете dto своим объектом, и вы должны быть готовы к работе.

Комментарии (0)

Пожалуйста, обратитесь к коду ниже, Вы можете управлять несколькими кодами состояния с другим типом JSON

public async Task 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(HttpStatusCode.NotFound, "No resources found.");
            }

            return this.Request.CreateResponse(HttpStatusCode.OK, resourceModelList, "application/json");
        }
    }
    catch (Exception ex)
    {
        return this.Request.CreateResponse(HttpStatusCode.InternalServerError, "Something went wrong.");
    }
}
Комментарии (1)