Подтвердить что ты не робот

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

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

enter image description here

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

4b9b3361

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

Часть кода выше в основном является дубликатом метода AddMvc(). Однако мы каждый раз внедряем каждый сервис по умолчанию, определяя каждую услугу, а не отправляя ее с предварительно отгруженным шаблоном. Я добавил ссылку репозитория в блок кода, или вы можете проверить 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-Type и Accept

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

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

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

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

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

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

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

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

options.RespectBrowserAcceptHeader = true; // false by default

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

Более подробную информацию можно найти в этом

Ответ 2

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

  • Ok(result) возвращает 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(object).
  4. Второй метод делает то же самое, используя атрибут ResultFilter Produces() (который является ResultFilter) с contentType = application/json

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Вместо использования кодов состояния 404/201 с помощью 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 !!!"));
    }

Ответ 7

Что я делаю в своих приложениях 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), которую вы заменяете dto на ваш объект, и вы должны хорошо идти.

Ответ 8

Потрясающие ответы, которые я нашел здесь, и я также попробовал это выражение return, см. StatusCode(whatever code you wish) и это сработало !!!

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

Ответ 9

См. ниже код. Вы можете управлять несколькими кодами состояния с помощью JSON другого типа.

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