Я ищу правильный способ вернуть 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.
Самая базовая версия, отвечающая 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
Давайте удостоверимся, что у вас есть некоторые основы...
Чтобы ваш 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 ()
из репозитория GitHub..
Я собираюсь показать вам действительно простой, чтобы разобраться в вашем вопросе.
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();
}
}
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
У вас есть предопределенные методы для большинства распространенных кодов состояния.
Ok (результат)
возвращает 200
с ответомCreatedAtRoute
возвращает 201
+ новый URL ресурсаNotFound
возвращает 404
BadRequest
возвращает 400
и т. Д.См. BaseController.cs
и Controller.cs
для списка всех методов.
Но если вы действительно настаиваете, что можете использовать StatusCode
для установки пользовательского кода, но вы действительно не должны этого делать, поскольку это делает код менее читабельным, и вам придется повторять код для установки заголовков (как для CreatedAtRoute
).
public ActionResult IsAuthenticated()
{
return StatusCode(200, "123");
}
С ASP.NET Core 2.0 идеальный способ возврата объекта из Web API
(который унифицирован с MVC и использует тот же базовый класс Controller
)
public IActionResult Get()
{
return new OkObjectResult(new Item { Id = 123, Name = "Hero" });
}
Обратите внимание, что
200 OK
(это тип Ok
ObjectResult
)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" };
}
Обратите внимание, что
Json (объект)
.Produces ()
(который является ResultFilter
) с contentType = application / json
Узнайте больше о них в официальных документах. Узнайте о фильтры здесь.
Простой модельный класс, который используется в образцах
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
Самый простой способ, которым я придумал, это:
var result = new Item { Id = 123, Name = "Hero" };
return new JsonResult(result)
{
StatusCode = StatusCodes.Status201Created // Status code here
};
Это мое самое простое решение:
public IActionResult InfoTag()
{
return Ok(new {name = "Fabio", age = 42, gender = "M"});
}
или
public IActionResult InfoTag()
{
return Json(new {name = "Fabio", age = 42, gender = "M"});
}
Вместо использования кодов состояния 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 !!!"));
}
Потрясающие ответы, которые я нашел здесь, и я также попробовал это ответное утверждение, см. «StatusCode (какой бы код вы ни пожелали)», и оно сработало!!!
return Ok(new {
Token = new JwtSecurityTokenHandler().WriteToken(token),
Expiration = token.ValidTo,
username = user.FullName,
StatusCode = StatusCode(200)
});
Что я делаю в своих приложениях 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 своим объектом, и вы должны быть готовы к работе.
Пожалуйста, обратитесь к коду ниже, Вы можете управлять несколькими кодами состояния с другим типом 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.");
}
}