前言

一年多没更新博客,上一次写此系列还是四年前,虽迟但到,没有承诺,主打随性,所以不存在断更,催更,哈哈,上一篇我们细究从请求到绑定详细原理,本篇则是探讨模型绑定细节,当一个问题产生到最终解决时,回过头我们整体分析其产生背景以及设计思路才能有所获。好了,废话不多说,我们开始模型绑定细节之旅。

问题产生

我们定义一个模型,然后进行查询请求,当然,此时我们在后台控制器Action方法上推荐明确使用查询特性即FromQuery接收,代码如下

public class UserAddress
{
public string Code { get; set; }
}
[ApiController]
[Route("api/[controller]/[action]")]
public class UserAddressController : ControllerBase
{
private readonly ILogger<UserAddressController> _logger; public UserAddressController(ILogger<UserAddressController> logger)
{
_logger = logger;
} [HttpGet]
public IActionResult Get([FromQuery] UserAddress address)
{
return Ok(address);
}
}

没任何毛病,接下来我们在定义用户地址类上增加一个属性,如下所示

public class UserAddress
{
public string Code { get; set; }
public string Address { get; set; }
}

值绑定不上,这是神马情况,这难道是官方的Bug吗,我们用6.0和7.0都是如此,毫无疑问,利用.NET 8.0依然是此等结果,问题来了,请稍加思考大概是什么原因,让我们继续往下分析

根因源码分析

通过前后对比我们可以初步分析到原因可能是二方面之一或者二者结合,其一,对象属性address和接收对象参数变量address不能相同(不区分大小写),其二,接受对象参数变量address和URL上的键名称address不能相同(不区分大小写)。我们暂且只能分析到这个地方,当然,我们一试便知,至于根因是什么,接下来我们只能去分析模型绑定源码,说到分析源码,可能有些童鞋不知从何开始,这里给出我们从0开始分析其根因的整个过程,以供需要的童鞋做参考哈。仅我个人看法,除非精通,否则必会经历一个过程,这是必然,所以不用怀疑任谁都不能立马找到大概源码在哪里,我们注意关注点分析,别看着看着跑偏了,既然是模型绑定而且是查询绑定,这是在了解基本原理或学习官网文档有所印象的前提下,先看这里

然后我们怎么开始呢,我们直接自定义实现一个查询字符串值绑定即将上述代码拷贝一份出来,比如有些是有依赖的等等,将其修改去掉等等处理,还是我们强调的关注点,最后我们还要添加自定义实现

   builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Insert(0, new QueryStringValueProviderFactory());
});

我们看到实际上值都已正确获取到,但实际上传过来的键应该是属性Code或者Address才对,同时我们发现在此实现中包含了一个是否包含前缀的方法,这好像貌似就是针对我们绑定的属性加上方法上的参数变量即address,所以我们断点一步步调试进入该方法具体实现

源码调试现在还是方便了很多,我们来到绑定源头即将ActionContext转换为ModelBindingContext,也就是调用具体绑定实现之前即相关参数绑定准备前夕,我们看到赋值给了模型绑定上下文中的模型名称即ModelName,我们猜测这就是增加的前缀,继续往下调试实际调用的绑定者是哪一个,我们看到实际使用的复杂对象绑定,框架内置实现了十几个绑定,ValueProvider只是其中后台接收最简单的参数类型或者直接接收请求上下文相关的预处理,大多都由ModelBinder来接收处理绑定到控制器方法上,调试源码并不是那么明朗,我们直接再自定义实现一个ComplexObjectModelBinderProvider,其具体ComplexObjectModelBinder有个方法BindPropertiesAsync,这是实际做相关处理的地方

/// <summary>
/// Create a property model name with a prefix.
/// </summary>
/// <param name="prefix">The prefix to use.</param>
/// <param name="propertyName">The property name.</param>
/// <returns>The property model name.</returns>
public static string CreatePropertyModelName(string? prefix, string? propertyName)
{
if (string.IsNullOrEmpty(prefix))
{
return propertyName ?? string.Empty;
} if (string.IsNullOrEmpty(propertyName))
{
return prefix ?? string.Empty;
} if (propertyName.StartsWith('['))
{
// The propertyName might represent an indexer access, in which case combining
// with a 'dot' would be invalid. This case occurs only when called from ValidationVisitor.
return prefix + propertyName;
} return prefix + "." + propertyName;
}

好了,到了这里我们只是知道了框架就是这么做的处理导致值绑定不上,问题又来了,请思考框架这么设计的初衷和思想是什么呢,框架为我们考虑了诸多场景。我们删除上述所有自定义实现, 框架以为我们想要达到如下绑定目的,但没曾想剑走偏锋,实际被我们钻了个空子,正所谓你以为的是你以为的并不是我以为的,然后一脸懵波

举一反三

还没完,继续开课,我们分析完整个前因后果后,我们终于明白了IValueProvider接口中所说的前缀具体指的是什么意思,然后对于前缀匹配使用二分法算法,同理,我们也不难看出,上述是对象绑定处理,在相同条件下,对于集合亦是如此。

总结

当进行查询操作时请求URL上的键名称若和后台接收参数变量名称相同且不区分大小写,框架以为我们想要使用接收参数变量作为前缀来绑定值,在相同等等条件下,对于集合亦是如此,除非我们自定义实现一套,否则我们万不可将其定义为相同名称,如此会导致值绑定不上。

翻译

搜索

复制

你所不知道的ASP.NET Core进阶系列(三)的更多相关文章

  1. 你所不知道的ASP.NET Core MVC/WebApi基础系列(一)

    前言 最近发表的EF Core貌似有点多,可别误以为我只专攻EF Core哦,私下有时间也是一直在看ASP.NET Core的内容,所以后续会穿插讲EF Core和ASP.NET Core,别认为你会 ...

  2. 你所不知道的ASP.NET Core MVC/WebApi基础系列 (一)

    转自博客:https://www.cnblogs.com/CreateMyself/p/9235968.html 前言 最近发表的EF Core貌似有点多,可别误以为我只专攻EF Core哦,私下有时 ...

  3. 你所不知道的ASP.NET Core MVC/WebApi基础系列(二)

    前言 好久没冒泡了,算起来估计有快半年没更新博客了,估计是我第一次停更如此之久,人总有懒惰的时候,时间越长越懒惰,但是呢,不学又不行,持续的惰性是不行dei,要不然会被时光所抛弃,技术所淘汰,好吧,进 ...

  4. 你所不知道的ASP.NET Core MVC/WebApi基础系列 (二)

    转自博客:https://www.cnblogs.com/CreateMyself/p/10604293.html 前言 本节内容,我们来讲讲.NET Core当中的模型绑定系统.模型绑定原理.自定义 ...

  5. Asp.Net Core 进阶(三)—— IServiceCollection依赖注入容器和使用Autofac替换它

    Asp.Net Core 提供了默认的依赖注入容器 IServiceCollection,它是一个轻量级的依赖注入容器,所以功能不多,只是提供了基础的一些功能,要实现AOP就有点麻烦,因此在实际工作当 ...

  6. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  7. js值----你所不知道的JavaScript系列(6)

    1.数组 在 JavaScript 中,数组可以容纳任何类型的值,可以是字符串.数字.对象(object),甚至是其他数组(多维数组就是通过这种方式来实现的) .----<你所不知道的JavaS ...

  8. js类型----你所不知道的JavaScript系列(5)

    ECMAScirpt 变量有两种不同的数据类型:基本类型,引用类型.也有其他的叫法,比如原始类型和对象类型等. 1.内置类型 JavaScript 有七种内置类型: • 空值(null) • 未定义( ...

  9. 闭包----你所不知道的JavaScript系列(4)

    一.闭包是什么? · 闭包就是可以使得函数外部的对象能够获取函数内部的信息. · 闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. · 闭包就 ...

  10. 你真的会玩SQL吗?你所不知道的 数据聚合

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

随机推荐

  1. Pytorch 最全入门介绍,Pytorch入门看这一篇就够了

    本文通过详细且实践性的方式介绍了 PyTorch 的使用,包括环境安装.基础知识.张量操作.自动求导机制.神经网络创建.数据处理.模型训练.测试以及模型的保存和加载. 1. Pytorch简介 在这一 ...

  2. 【Leaflet专题篇】L.tileLayer图层顺序问题

    1 问题复现 使用L.tileLayer加载底图(A.B.C)并使用layerControl管理.在用L.tileLayer.wms添加wms服务(D),当切换ABC时会压盖D视频中右下角的wms服务 ...

  3. 记一次因为C#官方扩展导致自动补全出错的情况 (C# & Godot)

    现象 最近使用Vscode结合Godot使用时突然发现自动补全出问题了,发现一部分自动补全能弹出补全项目,但是确认后不起作用,还会吞掉弹出自动补全后输入的字符.大概是下图这样的感觉(截图时已修好,图为 ...

  4. 原来ES7~12分别增加了这些属性呀

    ES6也称为ES2015,于2015年发布,此后每年都有新增一些属性,分别命名为ES7~12,发布的年份分别对应2016年到2021年 ES7 includes方法 数组中新增了includes方法, ...

  5. Vue项目打包后放到SpringBoot项目里注意点

  6. servlet系列:简介和基本使用以及工作流程

    目录 一.简介 二.Servlet实现 三.基本使用 1.引入pom依赖 2.实现Servlet规范,重写service方法 3.配置web.xml 4.配置Tomcat 6.运行 四.Servlet ...

  7. 【问题解决】容器部署MySQL的数据在docker commit导出的镜像中丢失

    问题起因 最近公司有个甲方项目参加竞赛,要求在(基于kubeflow/arena)平台上部置应用,可以将MySQL打包在应用一起,也可以分开部署,没有提供volume相关的支持.大意是可以把初始好的数 ...

  8. 浅析三维模型OBJ格式轻量化处理常见问题与处理措施

    浅析三维模型OBJ格式轻量化处理常见问题与处理措施 在三维模型OBJ格式轻量化处理过程中,可能会遇到一些问题.以下是一些常见问题以及相应的解决方法: 1.文件大小过大: OBJ格式的三维模型文件通常包 ...

  9. Python 基础面试第三弹

    1. 获取当前目录下所有文件名 import os def get_all_files(directory): file_list = [] # os.walk返回一个生成器,每次迭代时返回当前目录路 ...

  10. Ascend C保姆级教程:我的第一份Ascend C代码

    本文分享自华为云社区<Ascend C保姆级教程:我的第一份Ascend C代码>,作者:昇腾CANN . Ascend C是昇腾AI异构计算架构CANN针对算子开发场景推出的编程语言,原 ...