草庐IT

.NET WebAPI 自定义 NullableConverter 解决可为空类型字段入参“”空字符触发转换异常问题

张晓栋 - .NET 技术博客 2023-03-28 原文

最近在项目中启用了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

有关.NET WebAPI 自定义 NullableConverter 解决可为空类型字段入参“”空字符触发转换异常问题的更多相关文章

  1. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  2. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  3. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  4. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  5. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  6. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  7. ruby - 将散列转换为嵌套散列 - 2

    这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[

  8. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  9. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

随机推荐