Шаблон проектирования для реализации принципа DRY для веб-API

В моем контроллере есть два следующих метода действий. Оба берут один и тот же параметр и проводят одинаковую проверку модели. он отличается только в одной строке, где он вызывает метод службы.

Есть ли лучший способ реорганизовать этот код?

[HttpPost]
public async Task<IActionResult> Search([FromBody]AggregateSearchCriteria criteria)
{
    if (criteria == null || !criteria.Aggregates.Any())
    {
        return BadRequest();
    }

    var providers = Request.Headers["providers"];

    if (providers.Equals(StringValues.Empty))
        return BadRequest();

    criteria.Providers = providers.ToString().Split(',').ToList();

    ModelState.Clear();

    TryValidateModel(criteria);

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    var result = await _searchService.Search(criteria);

    return Ok(result);
}

[HttpPost("rulebreak")]
public async Task<IActionResult> SearchRuleBreak([FromBody]AggregateSearchCriteria criteria)
{
    if (criteria == null || !criteria.Aggregates.Any())
    {
        return BadRequest();
    }

    var providers = Request.Headers["providers"];

    if (providers.Equals(StringValues.Empty))
        return BadRequest();

    criteria.Providers = providers.ToString().Split(',').ToList();

    ModelState.Clear();

    TryValidateModel(criteria);

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    var result = await _searchService.SearchRuleBreak(criteria);

    return Ok(result);
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
250
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете реализовать IValidatableObject для своей модели AggregateSearchCriteria и переместить в него всю логику проверки. Для поставщиков вы можете добавить его в свою модель и написать настраиваемый связыватель данных, который будет связывать значения из заголовков, также вы можете написать связыватель массива coma, который разделит ваши значения в массив.

public class AggregateSearchCriteria : IValidatableObject
{
    [FromHeader]
    public IList<string> Providers { get; set; } = new List<string>();

    public IList<string> Aggregates { get; set; } = new List<string>();
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var result = new List<ValidationResult>();
        if (!Providers.Any())
        {
            result.Add(new ValidationResult("No Providers", new[] { nameof(AggregateSearchCriteria.Providers) }));
        }
        if (!Aggregates.Any())
        {
            result.Add(new ValidationResult("No Aggregates", new[] { nameof(AggregateSearchCriteria.Aggregates) }));
        }
        return result;
      }
   }

    [HttpPost]
    public async Task<IActionResult> Search([FromBody]AggregateSearchCriteria criteria)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var result = await _searchService.Search(criteria);

        return Ok(result);
    }
Ответ принят как подходящий

Что-то вроде этого могло бы стать началом.

[HttpPost]
public async Task<IActionResult> Search([FromBody]AggregateSearchCriteria criteria)
{
    return await Common(criteria, c => _searchService.Search(c));
}

public async Task<IActionResult> SearchRuleBreak([FromBody]AggregateSearchCriteria criteria)
{
    return await Common(criteria, c => _searchService.SearchRuleBreak(c));
}

private async Task<IActionResult> Common(AggregateSearchCriteria criteria, Func<List<string>, Task<???>> action)
{
    if (criteria == null || !criteria.Aggregates.Any())
    {
        return BadRequest();
    }

    var providers = Request.Headers["providers"];

    if (providers.Equals(StringValues.Empty))
        return BadRequest();

    criteria.Providers = providers.ToString().Split(',').ToList();

    ModelState.Clear();

    TryValidateModel(criteria);

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    var result = await action.Invoke(criteria);

    return Ok(result);
}

Прости еще раз. может это быть модульное тестирование, какое-то издевательство и прочая ерунда?

Mukil Deepthi 27.11.2018 12:14

См. docs.microsoft.com/en-us/aspnet/web-api/overview/…

stuartd 27.11.2018 12:40

Шаблонный шаблон - лучший вариант в подобных ситуациях. Однако не имеет отношения к валидации. И вы также должны позаботиться о зависимости контроллера. Обратите внимание, что приведенный ниже код не будет работать автоматически без некоторых изменений.

public abstract class BaseSearch
{
    public Task<IActionResult> Apply(AggregateSearchCriteria criteria)
    {
        if (criteria == null || !criteria.Aggregates.Any())
        {
            return BadRequest();
        }

        var providers = Request.Headers["providers"];

        if (providers.Equals(StringValues.Empty))
            return BadRequest();

        criteria.Providers = providers.ToString().Split(',').ToList();

        ModelState.Clear();

        TryValidateModel(criteria);

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var result = await ServiceCall(criteria);

        return Ok(result);
    }

    protected abstract async IActionResult ServiceCall(AggregateSearchCriteria criteria);
}

public class Search : BaseSearch
{
    protected async Task<IActionResult> ServiceCall(AggregateSearchCriteria criteria)
    {
        return await _searchService.Search(criteria);
    }
}

public class SearchRuleBreak : BaseSearch
{
    protected async Task<IActionResult> ServiceCall(AggregateSearchCriteria criteria)
    {
        return await _searchService.SearchRuleBreak(criteria);
    }
}

Тогда при звонке:

[HttpPost]
public async Task<IActionResult> Search([FromBody]AggregateSearchCriteria criteria)
{
    return await new Search().Apply(criteria);
}

[HttpPost("rulebreak")]
public async Task<IActionResult> SearchRuleBreak([FromBody]AggregateSearchCriteria criteria)
{
    return await new SearchRuleBreak().Apply(criteria);
}

Примечание: По мере того, как сегодня языки становятся все более и более функциональными, подход «отправить функцию как параметр» также является допустимым, как предлагает @stuartd.

Спасибо. Я узнал шаблон шаблона из вашего примера. Да, пойдет с решением @stuartd.

Mukil Deepthi 27.11.2018 12:09

Другие вопросы по теме