JS/C#接口的API标准——驼峰式命名法与帕斯卡命名法

3
我们有一个系统,其中用C#编写的服务器实现了REST接口,客户端使用JS编写,大部分数据以JSON格式传递。
在某种程度上,这里存在驼峰式和帕斯卡式世界之间的冲突。
参数大多来自于服务器内的名称,并且全部采用帕斯卡命名法。
JS前端使用驼峰命名法编写,希望服务器接受此类参数,因为JSON来自JS世界。
什么样的解决方案是可接受的,为什么?

没有真正的正确答案。然而,我的建议是将后端对 API 不可见,并根据使用 API 的约定进行操作,例如在这种情况下,使用 js 约定。 - Keith Nicholas
1
让双方都使用camelCase,让双方都使用PascalCase,或编写一个解释器来在两者之间进行转换。这就是实际上可以说的全部内容。 - Abion47
听起来像是左手没有注意右手在做什么。 - charlietfl
注意,编写一个XHR拦截器以在JS端透明地转换所有请求并不困难。 - charlietfl
如果我理解您的问题正确,那么CamelCasePropertyNameResolver正是为此而设计的。只需在您的webapi配置中注册即可。这样可以让您的客户端自由使用JavaScript约定,而服务则自由使用C#约定。 - knobcreekman
2个回答

1
对于Web API方法的参数,请使用“camelCase”。我认为这是.NET中方法参数的建议命名约定。
对于属性,请使用JSON序列化属性
public class Person
{
    [JsonProperty("id")]
    public int Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }
}

序列化对象将会像这样:
{ "id": 10, "name": "My Name" }

使用这种方法,您的C#代码保持PascalCase,客户端(Javascript)将使用camelCase。

我之前不知道这个,但现在看来非常合理! - Thomas

0

我不确定这是否是完整的答案,因为不清楚您想要实现什么。

显然,C# 是帕斯卡命名法,而 JSON 是驼峰命名法。

因此,对于我们的 ASP.Net Web API 应用程序,我们已经为 Json.NET 实现了 DataConverter:

public class DataConverter : JsonConverter
{
    #region Overriding
    public override bool CanRead
    {
        get { return true; }
    }
    public override bool CanWrite
    {
        get { return true; }
    }
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return this.ReadValue(reader);
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        this.WriteValue(writer, value, serializer);
    }
    #endregion

    #region Assistants
    private object ReadValue(JsonReader reader)
    {
        while (reader.TokenType == JsonToken.Comment)
        {
            if (reader.Read() == false)
                throw new Exception("Unexpected end.");
        }

        switch (reader.TokenType)
        {
            case JsonToken.StartObject:
                return this.ReadObject(reader);

            case JsonToken.StartArray:
                return this.ReadList(reader);

            default:
                if (this.CheckPrimitive(reader.TokenType) == true)
                    return reader.Value;

                throw new Exception(string.Format(CultureInfo.InvariantCulture, "Unexpected token when converting ExpandoObject: {0}", reader.TokenType));
        }
    }
    private object ReadList(JsonReader reader)
    {
        List<object> collection = new List<object>();

        while (reader.Read() == true)
        {
            switch (reader.TokenType)
            {
                case JsonToken.Comment:
                    break;

                case JsonToken.EndArray:
                    return collection;

                default:
                    object value = this.ReadValue(reader);

                    collection.Add(value);
                    break;
            }
        }

        throw new Exception("Unexpected end.");
    }
    private object ReadObject(JsonReader reader)
    {
        IDictionary<string, object> expando = new ExpandoObject();

        while (reader.Read() == true)
        {
            switch (reader.TokenType)
            {
                case JsonToken.PropertyName:
                    string property = reader.Value.ToString().ToCase(Casing.Pascal);

                    if (reader.Read() == false)
                        throw new Exception("Unexpected end.");

                    object value = this.ReadValue(reader);

                    expando[property] = value;
                    break;

                case JsonToken.Comment:
                    break;

                case JsonToken.EndObject:
                    return expando;
            }
        }

        throw new Exception("Unexpected end.");
    }

    private void WriteValue(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (this.CheckPrimitive(value) == true)
        {
            writer.WriteValue(value);
            return;
        }

        if (value is Guid)
        {
            this.WriteValue(writer, (Guid)value, serializer);
            return;
        }

        if (value is MyType)
        {
            this.WriteValue(writer, (MyType)value, serializer);
            return;
        }

        if (value is IDynamicMetaObjectProvider && value is IDictionary<string, object>)
        {
            this.WriteObject(writer, (IDictionary<string, object>)value, serializer);
            return;
        }

        if (value is IEnumerable)
        {
            IEnumerable enumerable = value as IEnumerable;
            this.WriteArray(writer, enumerable, serializer);
            return;
        }

        this.WriteObject(writer, value, serializer);
    }
    private void WriteValue(JsonWriter writer, Guid guid, JsonSerializer serializer)
    {
        writer.WriteValue(guid.ToString());
    }
    private void WriteValue(JsonWriter writer, MyType myType, JsonSerializer serializer)
    {
        writer.WriteValue(myType.ToString());
    }
    private void WriteArray(JsonWriter writer, IEnumerable enumerable, JsonSerializer serializer)
    {
        writer.WriteStartArray();
        foreach (object value in enumerable)
        {
            this.WriteValue(writer, value, serializer);
        }
        writer.WriteEndArray();
    }
    private void WriteObject(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        foreach (PropertyInfo properties in value.GetType().GetProperties())
        {
            ParameterInfo[] parameters = properties.GetGetMethod().GetParameters();
            if (parameters.Length == 0)
            {
                writer.WritePropertyName(properties.Name.ToCase(Casing.Camel));

                this.WriteValue(writer, properties.GetValue(value), serializer);
            }
        }
        writer.WriteEndObject();
    }
    private void WriteObject(JsonWriter writer, IDictionary<string, object> value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        foreach (KeyValuePair<string, object> properties in value)
        {
            writer.WritePropertyName(properties.Key.ToCase(Casing.Camel)); // Implement own casing...

            this.WriteValue(writer, properties.Value, serializer);
        }
        writer.WriteEndObject();
    }

    private bool CheckPrimitive(JsonToken token)
    {
        switch (token)
        {
            case JsonToken.Integer:
            case JsonToken.Float:
            case JsonToken.String:
            case JsonToken.Boolean:
            case JsonToken.Null:
            case JsonToken.Undefined:
            case JsonToken.Date:
            case JsonToken.Bytes:
                return true;
        }
        return false;
    }
    private bool CheckPrimitive(object value)
    {
        if (value == null)
            return true;

        if (value is bool)
            return true;

        if (value is byte)
            return true;

        if (value is sbyte)
            return true;

        if (value is short)
            return true;

        if (value is ushort)
            return true;

        if (value is int)
            return true;

        if (value is uint)
            return true;

        if (value is long)
            return true;

        if (value is ulong)
            return true;

        if (value is float)
            return true;

        if (value is double)
            return true;

        if (value is decimal)
            return true;

        if (value is char)
            return true;

        if (value is string)
            return true;

        if (value is DateTime)
            return true;

        if (value is Enum)
            return true;

        return false;
    }
    #endregion
}

当JSON转换为C#对象时,它将"someProperty"转换为"SomeProperty",在相反的方向上,它将"SomeProperty"转换为"someProperty"。这使我们可以在API控制器中使用动态关键字(在ASP.Net Core项目的输入参数中需要属性[FromBody])。


1
是的,但本质上这是在浪费CPU时间来处理人类语义,不是吗? - Thomas
你可以进行测试,但它基于库本身使用的原始转换器。如果性能不足,则可以优化算法。 - Maxim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接