#c# #generics #asp.net-core
#c# #общие #asp.net-ядро
Вопрос:
Я написал универсальные методы для Get, Post и Put. Примером получения универсального метода является :
public async Task<object> GetAsync<T>(string uri, NamingStrategy namingStrategy)
{
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri))
{
return await ProcessAsync<T>(requestMessage, namingStrategy);
}
}
и ProcessAync является :
public async Task<object> ProcessAsync<T>(HttpRequestMessage request, NamingStrategy namingStrategy)
{
if (!string.IsNullOrEmpty(AuthToken))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken);
}
HttpResponseMessage response = await _client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
_logger.LogInformation("Request Succeeded");
var dezerializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = namingStrategy
}
};
T responseModel = JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync(), dezerializerSettings);
return responseModel;
}
else
{
return await GetFailureResponseModel(response);
}
}
затем я вызываю этот метод get подобным образом в моем SingletonClass
public async Task<object> GetShops(string category)
{
_logger.LogInformation("ClubMatas outgoing request: {RequestName}", nameof(GetShops));
return await _client.GetAsync<ShopsResponseModel>($"v2/shops?category={WebUtility.UrlEncode(category)}");
}
И этот метод вызывается в моем контроллере следующим образом
public async Task<ActionResult<object>> GetShops([FromQuery(Name = "category")]string category)
{
var response = await _httpClient.GetShops(category);
return ParseResponse<ShopsResponseModel>(response);
}
и ParseResponse является
protected ActionResult<object> ParseResponse<T>(object response)
{
if (response.GetType() == typeof(T))
{
return Ok(response);
}
else
{
return Error(response);
}
}
Как показывает цепочка вызовов, я ожидаю другую SuccessModel или FailureModel в моем ответе Api, и из-за этого я должен использовать object в качестве возвращаемого типа. Но у меня такое чувство, что я не должен использовать тип объекта для возврата. К вашему сведению, приведенная выше цепочка работает нормально. Я просто ищу больше рефакторинга или улучшения моего текущего потока. ищете более элегантное решение этой проблемы. Пожалуйста, предложите любое другое решение моей проблемы.
Обновление Я попробовал предложение @ChrisPratt об использовании интерфейса, но это решение не работает или, возможно, я делаю это неправильно. Итак, я создал этот пустой интерфейс
public interface IResult
{
}
И я расширил как свой ShopResponseModel
интерфейс, так и FailureResponseModel
IResult
интерфейс from, а также обновил методы, подобные этому.
public async Task<IResult> GetShops(string category)
{
_logger.LogInformation("ClubMatas outgoing request: {RequestName}", nameof(GetShops));
return await _client.GetAsync<IResult>($"v2/shops?category={WebUtility.UrlEncode(category)}");
}
и
public async Task<T> GetAsync<T>(string uri, NamingStrategy namingStrategy)
{
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri))
{
return await ProcessAsync<T>(requestMessage, namingStrategy);
}
}
и я обновил ProcessAsync
возвращаемый тип с object на T. Но получаю ошибки.
Комментарии:
1. Почему бы просто не создать исключение на случай, если что-то пойдет не так?
2. Но мне также нужен этот failureResponse на мобильном конце, потому что он содержит пользовательские сообщения, связанные с ошибкой, а также о том, как ее исправить. Так что выбрасывание исключения мне в этом случае не поможет.
3. Универсальный метод не должен использоваться
object
. Он также должен принимать возвращаемый тип в качестве параметра типа. Что касается использования типов результатов, таких как Rust, F #, все, что вам нужно, это чтобы оба конкретных типа наследовали от одного и того же базового интерфейса / типа. У него даже не должно быть никаких членов4. @Shabirjan вы не опубликовали определения типов success / failure .
GetShops
показывает, что вместоobject
того, чтобы вы должны использоватьShopsResponseModel
или что-то, что содержитShopsResponseModel
5. @Shabirjan вам все равно не нужно
object
. Самым простым решением было бы использовать общий интерфейс, напримерIResult
, без членов.
Ответ №1:
Но у меня такое чувство, что я не должен использовать тип объекта для возврата.
ДА. Не используйте object
для возвращаемых значений. Он практически бесполезен в качестве возвращаемого типа. Что вы должны делать, так это возвращать интерфейс. Например, вы можете сделать что-то вроде:
public interface IResponseStatusModel
{
bool Succeeded { get; }
int StatusCode { get; }
// etc.
}
Затем:
public class SuccessModel : IResponseStatusModel
public class FailureModel : IResponseStatusModel
Затем вы можете вернуть IResponseStatusModel
, и на основе интерфейса вы сможете взаимодействовать с любым свойством или методом, определенным в интерфейсе, независимо от того, какую модель вы фактически возвращаете.
Однако у вас действительно не должно быть отдельных классов для success / failure . Создание интерфейса, который в общем случае может позволить вам взаимодействовать с любым из них, в любом случае приведет к стиранию границ между ними. Вместо этого вы должны просто вернуть один тип модели, который имеет свойства, подобные приведенным выше, и, возможно, свойство list для ошибок и тому подобное. Например:
public class ProcessResult
{
public ProcessResult(int statusCode) : this(statusCode, null) {}
public ProcessResult(int statusCode, IEnumerable<string> errors)
{
Succeeded = statusCode < 300;
StatusCode = statusCode;
Errors = errors;
}
public bool Succeeded { get; private set; }
public int StatusCode { get; private set; }
public IEnumerable<string> Errors { get; private set; }
}
Это очень простой пример. Возможно, вам захочется расширить его и предоставить более надежное решение для определения того, была ли задача выполнена успешно или нет. Общая идея заключается в том, что вы предоставляете как можно больше релевантной информации о результате операции. Затем в вашем коде вы можете просто перейти Succeeded
и затем соответствующим образом обработать ситуацию:
if (result.Succeeded)
{
// do something on success
}
else
{
// do something on failure
}
Комментарии:
1. Спасибо за подробный ответ. Но у меня есть пара вопросов, во-первых, почему интерфейс имеет параметры? Прямо сейчас в контроллере я возвращаю либо SuccessModel, либо FailureResponseModel. Во-вторых, как я могу добавить этот интерфейс в свой поток общих вызовов?
2. пробовал ваше решение по использованию интерфейса, но получал ошибки. Проверьте обновленный раздел в моем вопросе.