2

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

 3 years ago
source link: https://lindexi.gitee.io/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
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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 ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者前往 CSDN 关注我的主页

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

无盈利,不卖课,做纯粹的技术博客

以下是广告时间

推荐关注 Edi.Wang 的公众号
lindexi%2F201985113622445

欢迎进入 Eleven 老师组建的 .NET 社区
lindexi%2F20209121930471745.jpg

以上广告全是友情推广,无盈利


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK