SnakeCase QueryParams .Net Core

问题描述 投票:0回答:2

问题在于 QueryParams 对象不是蛇形案例

        public async Task<IActionResult> Search (
        [FromQuery] ProjectFilterDto filterParams,
        [FromQuery(Name = "page_size")] int pageSize = 10,
        [FromQuery(Name = "page_index")] int pageIndex = 0)

enter image description here

所有其他内容,FromBody 和 Response 均采用蛇形语法

启动;

options.SerializerSettings.ContractResolver = new DefaultContractResolver()
{
  NamingStrategy = new SnakeCaseNamingStrategy(true, true)
};

有什么方法可以在不使用 FromQuery(Name="x") 的情况下使像 ProjectFilterDto 这样的查询参数成为蛇形案例吗?

asp.net-core-webapi
2个回答
8
投票

首先创建一个扩展方法,将给定的字符串转换为其蛇形版本。我们在下一步中将需要这个方法。

public static string ToSnakeCase(this string s)
{
    if (string.IsNullOrWhiteSpace(s))
    {
        return s;
    }

    s = s.Trim();

    var length = s.Length;
    var addedByLower = false;
    var stringBuilder = new StringBuilder();

    for (var i = 0; i < length; i++)
    {
        var currentChar = s[i];

        if (char.IsWhiteSpace(currentChar))
        {
            continue;
        }

        if (currentChar.Equals('_'))
        {
            stringBuilder.Append('_');
            continue;
        }

        bool isLastChar = i + 1 == length,
                isFirstChar = i == 0,
                nextIsUpper = false,
                nextIsLower = false;

        if (!isLastChar)
        {
            nextIsUpper = char.IsUpper(s[i + 1]);                    
            nextIsLower = !nextIsUpper && !s[i + 1].Equals('_');
        }

        if (!char.IsUpper(currentChar))
        {
            stringBuilder.Append(char.ToLowerInvariant(currentChar));

            if (nextIsUpper)
            {
                stringBuilder.Append('_');
                addedByLower = true;
            }

            continue;
        }

        if (nextIsLower && !addedByLower && !isFirstChar)
        {
            stringBuilder.Append('_');
        }

        addedByLower = false;

        stringBuilder.Append(char.ToLowerInvariant(currentChar));
    }

    return stringBuilder.ToString();
}

现在我们可以创建一个自定义值提供程序,使用我们上面定义的

ToSnakeCase()
扩展方法来查找蛇形查询参数。

public class SnakeCaseQueryValueProvider : QueryStringValueProvider, IValueProvider
{
    public SnakeCaseQueryValueProvider(
        BindingSource bindingSource, 
        IQueryCollection values, 
        CultureInfo culture) 
        : base(bindingSource, values, culture)
    {
    }

    public override bool ContainsPrefix(string prefix)
    {
        return base.ContainsPrefix(prefix.ToSnakeCase());
    }

    public override ValueProviderResult GetValue(string key)
    {
        return base.GetValue(key.ToSnakeCase());
    }
}

我们还必须为我们的价值提供者实现一个工厂:

public class SnakeCaseQueryValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var valueProvider = new SnakeCaseQueryValueProvider(
            BindingSource.Query,
            context.ActionContext.HttpContext.Request.Query,
            CultureInfo.CurrentCulture);

        context.ValueProviders.Add(valueProvider);

        return Task.CompletedTask;
    }
}

剩下要做的唯一一件事就是在 Startup 类的

ConfigureServices
方法中注册值提供者。

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddMvc(options =>
        {
            options.ValueProviderFactories.Add(new SnakeCaseQueryValueProviderFactory());
        }) 
        .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver
        {
            NamingStrategy = new SnakeCaseNamingStrategy()
        });
}

希望这有帮助!

我在这里写了一篇关于这一切的博客文章:http://www.sorting.se/enable-snake-cased-query-parameters-in-your-restfull-asp-net-core-web-api/


0
投票

只需将参数命名为蛇形案例,例如:

public ActionResult MyMethod([FromQuery] string page_size){
 ...
}

与使用蛇形外壳或分页器创建过滤器的方法相同:

public class PaginatedResponse<TItem>
{
    public int Page { get; set; }
    public int PageSize { get; set; }
    public long TotalCount { get; set; }
    public List<TItem> Results { get; set; }
}

public class Paginator
{
    [Range(1, int.MaxValue)]
    public int Page { get; set; } = 1;

    [Range(1, 100)]
    public virtual int Page_Size { get; set; } = 20;

    public async Task<PaginatedResponse<TItem>> PaginateAsync<TItem>(IQueryable<TItem> queryable)
    {
        var totalCount = await queryable.LongCountAsync();
        var results = await queryable.Skip((Page - 1) * Page_Size).Take(Page_Size).ToListAsync();
        return new PaginatedResponse<TItem>()
        {
            Page = Page,
            PageSize = Page_Size,
            TotalCount = totalCount,
            Results = results,
        };
    }
}

然后你可以像这样使用它:

public async Task<ICollection<MyModel>> GetAllAsync([FromQuery] Paginator paginator){
  ...
  return await paginator.PaginateAsync(myQueriable)
}
© www.soinside.com 2019 - 2024. All rights reserved.