专栏首页林德熙的博客ASP.NET Core 警惕可空类型开启之后模型校验失败

ASP.NET Core 警惕可空类型开启之后模型校验失败

在开启 Nullable 可空类型之后,原本可以调用的 API 也许就会提示 400 BadRequest 因为传入参数不合法,模型校验失败,此时将不会进入预期的 API 函数,同时也不会在输出里面找到有用的信息

在 SDK 风格的 csproj 文件开启可空类型可以添加下面代码

      <Nullable>enable</Nullable>

为了方便让小伙伴知道上面代码加在哪里,我贴出更多的 csproj 文件代码

  <PropertyGroup>
      <TargetFramework>netcoreapp3.1</TargetFramework>
      <Nullable>enable</Nullable>
  </PropertyGroup>

在开启之后,原本工作的很好的 API 也许在客户端调用的时候,将会提示 400 BadRequest 内容大概如下

{
    "type":"https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title":"One or more validation errors occurred.",
    "status":400,
    "traceId":"00-99b1c07693a55c4990389901832992a4-b2ed63ee45e85344-01",
    "errors":
    {
        "Account":
        [
            "The Account field is required."
        ]
    }
}

复习一下为什么会存在 400 错误

  • 也许调用的 API 错误了,本来是预期调用 Foo 的,但却调用了 A 接口
  • 也许调用的端口不对,也许是被 Fiddler 干扰了
  • 也许是传入的参数不合法

如上面提示,实际内容是 The Account field is required 翻译过来就是接口里面的参数,要求一定存在 Account 属性

而明明之前工作的好好的,接口实现如下

        [HttpPost]
        // ReSharper disable once StringLiteralTypo
        [Route("/lindexi/doubi")]
        [RequestSizeLimit(100_000_000)]
        public async Task<string> PostFile([FromForm] LindexiUploadFileRequest request)

也就是需要通过 FromForm 拿到内容,而 LindexiUploadFileRequest 的定义如下

    [DataContract]
    public class LindexiUploadFileRequest
    {
        [DataMember(Name = "file")]
        [JsonPropertyName("file")]
        public IFormFile File { get; set; }

        [DataMember(Name = "account")]
        [JsonPropertyName("account")]
        public string Account { get; set; }
    }

客户端调用代码大概如下

       public async Task<string> Upload(string host, string file)
        {
            var multipartFormDataContent = new MultipartFormDataContent();
            var fileName = Path.GetFileName(file);

            var stringContent = new StringContent(fileName);
            multipartFormDataContent.Add(stringContent, "Name");

            using var fileStream = new FileStream(file, FileMode.Open);
            using var streamContent = new StreamContent(fileStream);
            multipartFormDataContent.Add(streamContent, "File", fileName);

            var account = "";

            multipartFormDataContent.Add(new StringContent(account), "Account");

            var httpClient = new HttpClient();
            var url = $"{host}/lindexi/doubi";

            var response = await httpClient.PostAsync(url, multipartFormDataContent).ConfigureAwait(false);
            var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
            return $"{response.StatusCode}\n{content}";
        }

小伙伴是否可以看出问题?实际上在开启可空之后,尽管在客户端代码里面设置了 multipartFormDataContent.Add(new StringContent(account), "Account"); 但是传入的内容是空字符串

而开启可空之后,定义的数据模型 public string Account { get; set; } 表示 Account 一定不是空,于是传入空的 Account 属性将会校验不通过

有两个解决方法,第一个解决方法就是标记 Account 属性可空

        [DataMember(Name = "account")]
        [JsonPropertyName("account")]
        public string? Account { get; set; }

但是对于大项目,很难测试全,此时可以在全局配置,让行为和之前相同

services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

打开 Startup.cs 文件,在 ConfigureServices 函数添加上面代码即可

但对于 EF 这边,有更多的变更,详细请看 Working with nullable reference types - EF Core

因此如果是新项目,我推荐开启可空,而对于现有的项目,我不推荐打开

MvcOptions.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes Property (Microsoft.AspNetCore.Mvc)

Nullable=Enabled results in required validation errors for parameters or bound properties with default values · Issue #18403 · dotnet/aspnetcore


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/ASP.NET-Core-%E8%AD%A6%E6%83%95%E5%8F%AF%E7%A9%BA%E7%B1%BB%E5%9E%8B%E5%BC%80%E5%90%AF%E4%B9%8B%E5%90%8E%E6%A8%A1%E5%9E%8B%E6%A0%A1%E9%AA%8C%E5%A4%B1%E8%B4%A5.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • win10 uwp 使用 asp dotnet core 做图床服务器客户端 服务器端客户端

    本文告诉大家如何在 UWP 做客户端和 asp dotnet core 做服务器端来做一个图床工具

    林德熙
  • WPF 解决 ListView 的滚动条不显示

    本文告诉大家如何解决一个诡异的问题,如果有一个 ListView 同时里面的元素的高度很长,但是滚动条就是不显示,怎么让这个滚动条显示

    林德熙
  • C# 性能分析 反射 VS 配置文件 VS 预编译

    本文分析在 C# 中使用反射和配置文件和预编译做注入的性能,本文的数据是为预编译框架,开发高性能应用 - 课程 - 微软技术暨生态大会 2018 - walte...

    林德熙
  • 云原生视角下的开放网络

    随着容器等轻量级高效率虚拟化技术的兴起与微服务理念的普及,云计算正向着“云原生”(Cloud Native)的方向发展。为了适应这个趋势,网络也需要进行相应的改...

    SDNLAB
  • 远程采集服务器指标信息(二)通过telnet执行远程命令

    现介绍java通过telnet执行命令采集服务器信息,比如说执行df、ls、top。

    一笠风雨任生平
  • JQuery中DOM操作[续]

    10、设置和获取HTML、文本和值示例代码 <p title=”选择你喜欢的水果?”><b>选择你喜欢的水果?</b></p> <form action=”” ...

    苦咖啡
  • \x 开头编码的数据解码成中文

    在python里,直接decode('utf-8')即可 >>> "\xE5\x85\x84\xE5\xBC\x9F\xE9\x9A\xBE\xE5\xBD\x...

    用户1177380
  • 服务器时间做好啦

    超级大猪
  • Android 高仿微信发朋友圈浏览图片效果

    版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/50...

    Hankkin
  • js算法初窥05(算法模式02-动态规划与贪心算法)

    zaking

扫码关注云+社区

领取腾讯云代金券