.NET WebAPI 自定義 NullableConverter 解決可為空型別欄位入參「」空字元觸發轉換異常問題

2022-09-21 06:00:38

最近在專案中啟用了Nullable 可為空的型別,這個特性確實很好用,在 WebAPI 的入參上可以直接採用 ? 來標記一個欄位是否允許為空,但是使用過程中遇到了如下一個問題,比如建立部門介面

我們定義入參模型如下:

public class DtoDepartment
{
    /// <summary>
    /// 部門名稱
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 上級部門ID
    /// </summary>
    public Guid? ParentId { get; set; }
}

我們這裡將上級部門ID定義為可以為空的型別,因為有些部門不存在上級部門
然後定義這樣一個介面

[HttpPost("CreateDepartment")]
public bool CreateDepartment(DtoDepartment department)
{
    //省略業務邏輯
    return true;
}

當前端請求的時候傳入如下Json 時則就會觸發異常

{
  "name": "商務一部",
  "parentId": ""
}

異常內容為:

{
  "errMsg": "The department field is required. | The JSON value could not be converted to System.Nullable`1[System.Guid]. Path: $.parentId | LineNumber: 2 | BytePositionInLine: 16."
}

像這樣的情況是因為雖然我們定義的 Dto 允許上級部門ID欄位為空,但是前端呼叫的時候 parentId 實際傳入的是 "" 空字串,當空字串給 Guid? 轉換的時候就會產生這樣的異常,當遇到這樣的情況時,我們可以要求前端調整 JSON 格式如下

{
  "name": "商務一部",
  "parentId": null
}

前端只要給 parentId 的賦值從 "" 調整為 null 之後我們的介面就可以正常執行了,但是有的時候前端的元件這裡取值可能是和一些元件庫繫結的,不太方便繫結預設值為 null,很多情況下元件庫元件的預設值都是 "" 空字串的形式,所以經過和前端同事多次溝通之後想著從後端徹底解決這個問題,經過研究之後編寫了下面的 NullableConverter 轉換器,只要在專案啟動的時候註冊到 AddJsonOptions 其中即可。

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Common.JsonConverter
{
    public class NullableConverter<T> : JsonConverter<T?> where T : struct
    {

        public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.String)
            {
                if (string.IsNullOrEmpty(reader.GetString()) || string.IsNullOrWhiteSpace(reader.GetString()))
                {
                    return null;
                }
            }
            return JsonSerializer.Deserialize<T>(ref reader, options);
        }


        public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
        {
            JsonSerializer.Serialize(writer, value!.Value, options);
        }

    }
}

上面我們只是用 Guid? 舉了一個例子,實際情況下

  • DateTime?
  • DateTimeOffset?
  • long?
  • int?
  • double?
  • decimal?
  • float?
  • Guid?
  • bool?

都有可能存在這個問題,所以我們為這幾種型別都設定了這個可為空型別轉換器。這樣前端在呼叫介面時配到這型別的欄位,傳 "" 和 null 我們後端就都可以接收了,收到之後欄位的值都是 null

我這裡專案採用的是微軟的 System.Text.Json 處理的 Json 序列化,註冊設定 NullableConverter 程式碼如下:

#region 註冊 Json 序列化設定
builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<DateTime>());
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<DateTimeOffset>());
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<long>());
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<int>());
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<double>());
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<decimal>());
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<float>());
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<Guid>());
    options.JsonSerializerOptions.Converters.Add(new Common.JsonConverter.NullableConverter<bool>());
});
#endregion

至此 .NET WebAPI 自定義 NullableConverter 解決可為空型別欄位入參「」空字元觸發轉換異常問題 就講解完了,有任何不明白的,可以在文章下面評論或者私信我,歡迎大家積極的討論交流,有興趣的朋友可以關注我目前在維護的一個 .NET 基礎框架專案,專案地址如下
https://github.com/berkerdong/NetEngine.git
https://gitee.com/berkerdong/NetEngine.git