Шаблон проектирования для реализации принципа DRY для Web API - PullRequest
0 голосов
/ 27 ноября 2018

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

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

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

Ответы [ 3 ]

0 голосов
/ 27 ноября 2018

Примерно так может быть началом.

[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);
}
0 голосов
/ 27 ноября 2018

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

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.

0 голосов
/ 27 ноября 2018

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

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