#c# #asp.net-web-api
#c# #asp.net-web-api
Вопрос:
Я пытаюсь найти способ вернуть объект в моем ответе, сохраняя при этом понятный возвращаемый тип.
Итак, для начала, я знаю, что это работает так, как ожидалось.
public async Task<HttpResponseMessage> DoMyThing(MyObject myObject)
{
var result = await _myService.CreateMyThingAsync(myObject);
return Request.CreateResponse(HttpStatusCode.Created, result);
}
Но чего я действительно хочу, так это чтобы этот псевдокод работал … каким-то образом.
public Task<MyObject> DoMyThing(MyObject myObject)
{
var result = _myService.CreateMyThingAsync(myObject);
return Request.CreateResponse<Task<MyObject>>(HttpStatusCode.Created, result);
// or better yet
return Request.CreateResponse<MyObject>(HttpStatusCode.Created, result);
}
Есть ли какая-либо магия в фреймворке, которая заставит это произойти? Или есть какие-либо сторонние библиотеки, которые могут это сделать?
По сути, мне нужно вернуть Task<MyObject>
вместо Task<HttpResponseMessage>
Я также открыт для других предложений о том, как вернуть ответ, отличный от 200, при этом возвращая Task<MyObject>
Комментарии:
1. Есть какая-либо конкретная причина, по которой вы хотите, чтобы контроллер возвращал простые объекты?
2. @Nkosi Я создаю контракт между
MyService
,MyProxyService
,MyController
, которые все наследуют отIMyService
3. Это плохая идея. Совет. Контроллер представляет собой абстракцию уровня пользовательского интерфейса. Его ответственность заключается в обеспечении правильности данных входящего запроса и выборе того, какой вид (или результат для API) должен быть возвращен. В хорошо продуманных приложениях это не будет напрямую включать доступ к данным или бизнес-логику, но вместо этого будет делегироваться службам, выполняющим эти обязанности.
4. @Nkosi, в наших случаях мы не используем его как абстракцию уровня пользовательского интерфейса. Сервисы представляют собой контракт между различными клиентами, каждый клиент имеет свой собственный уровень пользовательского интерфейса над сервисом и репозиторий под сервисом.
5. В принципе, мы всегда ожидаем, что действительный запрос выдаст общий ответ. Если возникает ошибка, мы не используем запеченные в
NotFound()
и др., но вместо этого мы просто создаем исключение и позволяем глобальному обработчику обрабатывать сопоставление исключения с конкретным кодом ответа (400, 404 и т.д.).
Ответ №1:
Проблема с указанием типа в качестве возвращаемого типа заключается в том, что вы ограничиваете себя необходимостью возвращать этот тип. Это может показаться странным, но на самом деле будет много случаев, когда вам нужно будет поддерживать множественный ответ, такой как 404, 200 201 и так далее.
Для обработки документации по этому вопросу вы можете использовать атрибут responseType, например:
[ResponseType(typeof(BookDto))]
public async Task<IHttpActionResult> GetBook(int id)
{
BookDto book = await db.Books.Include(b => b.Author)
.Where(b => b.BookId == id)
.Select(AsBookDto)
.FirstOrDefaultAsync();
if (book == null)
{
return NotFound();
}
return Ok(book);
}
Взгляните сюда
Редактировать:
В Asp.Net В основном вы используете атрибут ProducesResponseType, который может использоваться несколько раз для каждого метода
Смотрите здесь
Пример
[ProducesResponseType(typeof(BookDto), 200)]
[ProducesResponseType(typeof(object), 201)]
public async Task<IActionResult> GetBook(int id)
{
BookDto book = await db.Books.Include(b => b.Author)
.Where(b => b.BookId == id)
.Select(AsBookDto)
.FirstOrDefaultAsync();
if (book == null)
{
return NotFound();
}
return Ok(book);
}
РЕДАКТИРОВАТЬ: Несколько атрибутов ответа до dot net core
Вы можете использовать Swagger для документирования / описания вашего API, у них есть пользовательский атрибут, называемый SwaggerResponse
Портом Swagger для .Net является Swashbuckle, взгляните сюда
Комментарии:
1. есть ли что-то вроде
ProducesResponseType
без Asp.Net Ядро? В принципе, мне нужно иметь возможность изменять код ответа.2. Обновлено ссылкой на Swashbuckle, который является отличным способом документирования ваших API
3. О, вот и все! Мы уже используем Swagger, так что это сработает. Я работаю в основном над нашим мобильным приложением, так что это мой первый опыт работы с нашим API. Мой опыт тоже связан с NancyFX, так что это добавляет мне путаницы. Спасибо!
Ответ №2:
Это было бы лучшим шаблоном в WebAPI.
public async Task<IHttpActionResult> DoMyThing(MyObject myObject)
{
try
{
var result = await _myService.CreateMyThingAsync(myObject);
return new JsonStreamHttpActionResult<MyObject>(Request, System.Net.HttpStatusCode.Created, result);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
return new HttpActionResult(HttpStatusCode.InternalServerError, "An error has occured");
}
}
с помощью универсального сериализатора. Затем вы можете использовать «лучший» IHttpActionResult вместо реального возвращаемого значения.
public class JsonStreamHttpActionResult<T> : IHttpActionResult
{
private T responseData;
private HttpRequestMessage request;
private HttpStatusCode statusCode;
public JsonStreamHttpActionResult(HttpRequestMessage request, System.Net.HttpStatusCode code, T responseData)
{
this.responseData = responseData;
this.request = request;
this.statusCode = code;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = request.CreateResponse(statusCode);
response.Content =
new PushStreamContent((stream, content, context) =>
{
var serializer = new Newtonsoft.Json.JsonSerializer();
using (var writer = new System.IO.StreamWriter(stream))
{
serializer.Serialize(writer, responseData);
stream.Flush();
}
});
return Task.FromResult(response);
}
}
Комментарии:
1. но я все еще хочу сохранить
Task<MyObject>
возвращаемый тип. вместоTask<HttpResponseMessage>
.2. Да, это невозможно, если вы не используете [responseType(typeof(BookDTO))] и Swashbuckle поверх него для создания документации. Вы не можете вернуть HttpResponse и тип одновременно. Предполагается, что контроллер возвращает HttpResponse в конечном счете, если вы возвращаете обычный тип, вы позволяете платформе MVC создать ответ за вас.