Web Api 参数绑定:snake_case 到camelCase

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

我正在一个新的 Web api 项目上做一些工作,我的方法/参数遵循标准的 c# 命名格式;方法名是PascalCase,参数是camelCase。

不幸的是,我刚刚发现客户端的文档已经发布,所有参数都是 ruby/php 风格,参数名称为 Snake_case 类型。

返回对象和POST对象易于转换;我使用了crallen代码的一个版本来替换默认的Json输入/输出,但是在GET请求上,似乎没有这么简单的答案。

我希望保持命名约定不变。有没有办法告诉绑定器自动将所有请求的 my_parameter 更改为 myParameter?我必须构建一个完全不同的活页夹吗?

例如,如果我将此作为方法签名:

[Route("~/api/Widgets")]
[ResponseType(typeof(Widget))]
public async Task<HttpResponseMessage> GetWidget(int widgetId, int groupId)
{
    . . .

我希望能够在 URL 中使用它

https://myserver.com/api/Widgets?widget_id=12345&group_id=54321

我是否必须重新发明轮子才能使其发挥作用?我见过替换特定类型模型绑定程序的示例,但在这个级别上什么也没有。我最好只更改代码中的参数名称吗?

c# asp.net-web-api model-binding
4个回答
6
投票

您可以通过使用自定义

ApiControllerActionSelector
重写
Request.RequestUri
然后调用基本选择器来实现此目的。

就这样:

首先,创建自定义选择器:

public class SnakeCaseActionSelector : ApiControllerActionSelector
{
    public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
    {
        var newUri = CreateNewUri(
            controllerContext.Request.RequestUri, 
            controllerContext.Request.GetQueryNameValuePairs());

        controllerContext.Request.RequestUri = newUri;

        return base.SelectAction(controllerContext);
    }

    private Uri CreateNewUri(Uri requestUri, IEnumerable<KeyValuePair<string, string>> queryPairs)
    {
        var currentQuery = requestUri.Query;
        var newQuery = ConvertQueryToCamelCase(queryPairs);
        return new Uri(requestUri.ToString().Replace(currentQuery, newQuery));
    }

    private static string ConvertQueryToCamelCase(IEnumerable<KeyValuePair<string, string>> queryPairs)
    {
        queryPairs = queryPairs
            .Select(x => new KeyValuePair<string, string>(x.Key.ToCamelCase(), x.Value));

        return "?" + queryPairs
            .Select(x => String.Format("{0}={1}", x.Key, x.Value))
            .Aggregate((x, y) => x + "&" + y);
    }
}

接下来,创建一些扩展以转换为驼峰式大小写并转换为大写单词:

public static class StringExtensions
{
    public static string ToCamelCase(this string source)
    {
        var parts = source
            .Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);

        return parts
            .First().ToLower() +
            String.Join("", parts.Skip(1).Select(ToCapital));
    }

    public static string ToCapital(this string source)
    {
        return String.Format("{0}{1}", char.ToUpper(source[0]), source.Substring(1).ToLower());
    }
}

最后将操作选择器添加到

WebApiConfig

config.Services.Replace(typeof(IHttpActionSelector), new SnakeCaseActionSelector());

3
投票

我想提出一个使用自定义值提供者工厂的替代解决方案:

  1. 源自
    QueryStringValueProviderFactory
    并覆盖
    GetValueProvider
  2. 获取查询字符串对并进行相应修改。
  3. 返回
    NameValuePairsValueProvider
    的实例,接受新修改的值。
  4. QueryStringValueProviderFactory
    中原来的
    HttpConfiguration.Services
    替换为你自己的。

大致翻译为:

public class SnakeQueryStringValueProviderFactory : QueryStringValueProviderFactory
{
    public override IValueProvider GetValueProvider( HttpActionContext actionContext )
    {
        var pairs = actionContext.ControllerContext.Request.GetQueryNameValuePairs();

        // modify query string keys accordingly
        // e.g. remove '_' from query string keys 
        // var newPairs = ...

        return new NameValuePairsValueProvider( newPairs, CultureInfo.InvariantCulture );
    }
}

以下内容应该改进,但你明白了。

config.Services.Remove( 
    typeof( ValueProviderFactory ),
    config.Services.GetValueProviderFactories().First( f => f is QueryStringValueProviderFactory ) 
);
config.Services.Insert( typeof( ValueProviderFactory ), 0, new SnakeQueryStringValueProviderFactory() ); // should be first

0
投票

venerik,我认为您的 CreateNewUri 方法可以通过使用 UriBuilder 类而不是直接字符串替换来改进。这意味着您不必将查询字符串与“?”连接起来。在您的 ConvertQueryToCamelCase 方法中。当尝试替换 URL 中包含空格、冒号、百分号等编码符号的查询字符串时,它将解决该问题。

private Uri CreateNewUri(Uri requestUri, IEnumerable<KeyValuePair<string, string>> queryPairs)
{
    var currentQuery = requestUri.Query;
    var newQuery = ConvertQueryToCamelCase(queryPairs);
    var builder = new UriBuilder(requestUri) 
    {
        Query = newQuery;
    };
    return builder.Uri;
}

private static string ConvertQueryToCamelCase(IEnumerable<KeyValuePair<string, string>> queryPairs)
{
    queryPairs = queryPairs
        .Select(x => new KeyValuePair<string, string>(x.Key.ToCamelCase(), x.Value));

    return queryPairs
        .Select(x => String.Format("{0}={1}", x.Key, x.Value))
        .Aggregate((x, y) => x + "&" + y);
}    

0
投票

如果您不需要通用解决方案或只需要少数情况:

public async Task<HttpResponseMessage> GetWidget(
         [FromQuery(Name = "widget_id")] int widgetId, 
         [FromQuery(Name = "group_id")] int groupId)
{
© www.soinside.com 2019 - 2024. All rights reserved.