从编程的角度来看,一个MVC应用是由一系列Controller类型构建而成的,所以对于一个代表应用模型的ApplicationModel对象来说,它的核心就是Controllers属性返回的一组ControllerModel对象,每个ControllerModel对象是应用模型针对Controller类型的描述。

一、ControllerModel

二、 实例演示:Controller模型的构建

三、实例演示:定制Controller模型

一、ControllerModel

描述Controller类型的ControllerModel具有如下定义。该类型的Application属性返回作为当前应用模型的ApplicationModel对象。它的Actions属性返回的ActionModel是对所有定义在当前Controller类型中的Action方法的描述。描述Controller类型属性的PropertyModel对象则存放在ControllerProperties属性中,由于PropertyModel和描述Action方法参数的ParameterModel对象承载的都是服务于模型绑定的元数据,所以我们会将这两个类型的介绍放在一起。ControllerModel类型的Selectors属性返回的一组SelectorModel对象是对应用在Controller级别上的Action选择器的描述,我们会在后续内容中对SelectorModel对象进行单独介绍。

public class ControllerModel : ICommonModel, IFilterModel, IApiExplorerModel
{
public ApplicationModel Application { get; set; } public IList<ActionModel> Actions { get; }
public IList<PropertyModel> ControllerProperties { get; }
public IList<SelectorModel> Selectors { get; } public IDictionary<object, object> Properties { get; }
public IList<IFilterMetadata> Filters { get; }
public ApiExplorerModel ApiExplorer { get; set; } public TypeInfo ControllerType { get; }
public IReadOnlyList<object> Attributes { get; }
public string ControllerName { get; set; }
public string DisplayName { get; }
public IDictionary<string, string> RouteValues { get; } MemberInfo ICommonModel.MemberInfo { get; }
string ICommonModel.Name { get; }
}

ControllerModel类型同时实现了ICommonModel、IFilterModel和IApiExplorerModel接口。默认注册的DefaultApplicationModelProvider会对ControllerModel对象做如下的设置:ControllerType和MemberInfo属性会设置为当前Controller的类型,该类型名称去除“Controller”后缀的字符串会作为Name和ControllerName的属性值。通过标注的特性注册到Controller类型上的过滤器会被提取出来,对应的元数据会添加到Filters属性中。ApiExplorer属性返回的ApiExplorerModel对象由标注在Controller类型上实现了IApiDescriptionGroupNameProvider和IApiDescriptionVisibilityProvider接口的特性构建而成。

DefaultApplicationModelProvider还会将标注到Controller类型上的所有特性提取出来,并将它们添加到Attributes属性中。如果特性类型实现了IRouteTemplateProvider接口,它们将专门用来构建特性路由信息或者路由约束,所以它们会从此列表中移除。DisplayName属性返回的显示名称通过对类型名称作相应格式化生成。DefaultApplicationModelProvider还会提取标注在Controller类型上实现了IRouteValueProvider接口的特性,并利用对应的设置来填充RouteValues属性返回的路由参数。目前唯一实现了该接口的是如下这个用来设置Area名称的AreaAttribute特性,设置的路由参数名称为“area”。

public interface IRouteValueProvider
{
string RouteKey { get; }
string RouteValue { get; }
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=true, Inherited=true)]
public abstract class RouteValueAttribute : Attribute, IRouteValueProvider
{
public string RouteKey { get; }
public string RouteValue { get; }
protected RouteValueAttribute(string routeKey, string routeValue);
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public class AreaAttribute : RouteValueAttribute
{
public AreaAttribute(string areaName) : base("area", areaName)
{}
}

二、 实例演示:Controller模型的构建

我们照例通过一个简单的实例来演示应用模型中用以描述Controller的元数据采用的默认构建规则。我们在前面演示程序中定义如下这个测试Controller类型FoobarController。如代码片段所示,FoobarController类型上标注了三个特性,分别是用来指定Area的AreaAttribute、过滤器特性FoobarAttribute和设置ApiExplorer的ApiExplorerSettingsAttribute。FoobarController类型中定义了两个属性(A和B)和两个Action方法(Foo和Bar)。

[Area("test")]
[Foobar]
[ApiExplorerSettings(GroupName = "test")]
public class FoobarController
{
public string A { get; set; }
public string B { get; set; } [HttpGet("foo")]
public void Foo() => throw new NotImplementedException();
[HttpGet("bar")]
public void Bar() => throw new NotImplementedException();
}

为了在页面上呈现描述FoobarController类型的ControllerModel对象的相关信息,我们对定义在HomeControllere中的Action方法Index作了相应的修改。如下面的代码片段所示,我们利用在方法中注入的ApplicationModelProducer对象根据FoobarController类型创建一个ApplicationModel对象,并将包含在该对象中用来描述FoobarController的ControllerModel作为Model呈现成默认的View中。

public class HomeController: Controller
{
[HttpGet("/")]
public IActionResult Index([FromServices]ApplicationModelProducer producer)
{
var applicationModel = producer.Create(typeof(FoobarController));
return View(applicationModel.Controllers.Single());
}
}

我们最后对Action方法Index对应的View文件作相应的修改。如下面的代码片段所示,这是一个Model类型为ControllerModel的强类型View,,它将ControllerModel承载的元数据呈现在一个表格中。

@using  Microsoft.AspNetCore.Mvc.ApplicationModels;
@model ControllerModel
@{
var commonModel = (ICommonModel)Model;
var actions = Model.Actions;
var filters = Model.Filters;
var properties = Model.ControllerProperties;
var attributes = Model.Attributes;
var routeValues = Model.RouteValues.ToArray();
}
<html>
<head>
<title>Controller</title>
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0">
<tr><td>ControllerType </td><td>@Model.ControllerType.Name</td></tr>
<tr><td>ControllerName </td><td>@Model.ControllerName</td></tr>
<tr><td>Name </td><td>@commonModel.Name</td></tr>
<tr><td>DisplayName </td><td>@Model.DisplayName</td></tr>
<tr><td rowspan="@actions.Count">Actions</td><td>@actions[0].ActionName</td></tr>
@for (int index = 1; index < actions.Count; index++)
{
<tr><td>@actions[index].ActionName</td></tr>
}
<tr>
<td rowspan="@filters.Count">Filters</td><td>@filters[0].GetType().Name</td>
</tr>
@for (int index = 1; index < filters.Count; index++)
{
<tr><td>@filters[index].GetType().Name</td></tr>
}
<tr>
<td rowspan="@properties.Count">ControllerProperties</td>
<td>@properties[0].PropertyName</td>
</tr>
@for (int index = 1; index < properties.Count; index++)
{
<tr><td>@properties[index].PropertyName</td></tr>
}
<tr>
<td rowspan="@attributes.Count">Attributes</td>
<td>@attributes[0].GetType().Name</td>
</tr>
@for (int index = 1; index < attributes.Count; index++)
{
<tr><td>@attributes[index].GetType().Name</td></tr>
}
<tr>
<td rowspan="@routeValues.Length">RouteVlues</td>
<td>@routeValues[0].Key = @routeValues[0].Value</td>
</tr>
@for (int index = 1; index < routeValues.Length; index++)
{
<tr><td>@routeValues[index].Key = @routeValues[index].Value</td></tr>
}
<tr>
<td rowspan="2">ApiExplorer</td>
<td>IsVisible = @Model.ApiExplorer.IsVisible </td>
</tr>
<tr><td>GroupName = @Model.ApiExplorer.GroupName </td></tr>
</table>
</body>
</html>

改动后的演示程序启动后,我们利用浏览器访问应用的主页,可以得到如图1所示的输出结果。正如上面我们所说的,去除“Controller”字符后缀的类型名称成为了ControllerModel对象的Name和ControllerName的属性值(“Foobar”)。两个属性(A和B)和Action方法(Foo和Bar)转换成相应的PropertyModel和ActionModel对象并分别添加到ControllerModel对象的ControllerProperties和Actions属性中。通过标注特性注册的过滤器(FoobarAttribute)被添加到ControllerModel对象的Filters属性中。通过标注的AreaAttribute设置的Area名称最终转移到ControllerModel对象对象RouteValues属性中。ControllerModel对象的ApiExplorer属性返回的ApiExplorerModel对象很明显是通过标注在类型上的ApiExplorerSettingsAttribute特性创建的,而它的Attributes属性中包含了我们标注的三个特性。

图1Controller模型默认的构建规则

三、实例演示:定制Controller模型

通过前面介绍的针对应用模型的总体设计,我们知道针对Controller模型的定制可以通过自定义的IControllerModelConvention实现类型来实现,我们接下来利用这种方式来改变Controller默认的命名规则。我们在上面演示的程序中定义了如下这个ControllerNameAttribute特性,该特性类型实现了IControllerModelConvention接口,在实现的Apply方法中,我们将构造函数中设置的Controller名称应用到提供的ControllerModel对象上。我们将该特性标注到FoobarController类型上并将名称设置为“Baz”。

[AttributeUsage(AttributeTargets.Class)]
public class ControllerNameAttribute : Attribute, IControllerModelConvention
{
public string ControllerName { get; }
public ControllerNameAttribute(string name) => ControllerName = name;
public void Apply(ControllerModel controller) => controller.ControllerName = ControllerName;
} [Area("test")]
[Foobar]
[ApiExplorerSettings(GroupName = "test")]
[ControllerName("Baz")]
public class FoobarController
{

}

改动后的演示程序启动后,我们利用浏览器访问应用的主页,可以得到如图2所示的输出结果。我们从图中可以看出,对于最终生成的用来描述FoobarController类型的ControllerModel对象来说,它的ControllerName属性被设置成我们指定的名称“Baz”,它的Name属性(ControllerModel类型针对ICommonModel接口的实现)返回的就是ControllerName属性值。

图2 自定义IControllerModelConvention实现类型定制Controller模型

ASP.NET Core MVC应用模型的构建[3]: Controller的收集的更多相关文章

  1. ASP.NET Core MVC/WebAPi 模型绑定探索

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  2. ASP.NET Core MVC 之模型(Model)

    1.模型绑定 ASP.NET Core MVC 中的模型绑定将数据从HTTP请求映射到操作方法参数.参数既可以是简单类型,也可以是复杂类型.MVC 通过抽象绑定解决了这个问题. 2.使用模型绑定 当 ...

  3. ASP.NET Core MVC/WebAPi 模型绑定探索 转载https://www.cnblogs.com/CreateMyself/p/6246977.html

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  4. 【转】ASP.NET Core MVC/WebAPi 模型绑定探索

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  5. ASP.NET Core MVC/WebAPi 模型绑定

    public class Person { public string Name { get; set; } public string Address { get; set; } public in ...

  6. ASP.NET Core MVC 模型绑定用法及原理

    前言 查询了一下关于 MVC 中的模型绑定,大部分都是关于如何使用的,以及模型绑定过程中的一些用法和概念,很少有关于模型绑定的内部机制实现的文章,本文就来讲解一下在 ASP.NET Core MVC ...

  7. ASP.NET Core MVC+EF Core从开发到部署

    笔记本电脑装了双系统(Windows 10和Ubuntu16.04)快半年了,平时有时间就喜欢切换到Ubuntu系统下耍耍Linux,熟悉熟悉Linux命令.Shell脚本以及Linux下的各种应用的 ...

  8. ASP.NET Core MVC中URL和数据模型的匹配

    Http GET方法 首先我们来看看GET方法的Http请求,URL参数和ASP.NET Core MVC中Controller的Action方法参数匹配情况. 我定义一个UserController ...

  9. ASP.NET Core MVC如何上传文件及处理大文件上传

    用文件模型绑定接口:IFormFile (小文件上传) 当你使用IFormFile接口来上传文件的时候,一定要注意,IFormFile会将一个Http请求中的所有文件都读取到服务器内存后,才会触发AS ...

  10. ASP.NET Core 入门教程 4、ASP.NET Core MVC控制器入门

    一.前言 1.本教程主要内容 ASP.NET Core MVC控制器简介 ASP.NET Core MVC控制器操作简介 ASP.NET Core MVC控制器操作简介返回类型简介 ASP.NET C ...

随机推荐

  1. WebAssembly入门笔记[2]:利用Memory传递数据

    利用灵活的"导入"和"导出"机制,WebAssembly与承载的JavaScript应用之间可以很便利地"互通有无".<与JavaSc ...

  2. Vue3中readonly 和 shallowReadonly和toRaw

    1.readonly 深度只读 被readonly包裹的数据只能够读取. 是一个深度只读,不能够修改. 我们看一下面的代码. 我们想修改值,但是修改后视图无响应. 并且控制台警告目标为只读 reado ...

  3. 【解决了一个小问题】macbook m2 下交叉编译 musl-gcc 支持的 gozstd 库

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 我的 golang 项目中使用了 gozstd, 在 ma ...

  4. Git的使用(二):远程仓库

    在github上创建远程仓库 本地创建Git仓库适合自己一个人完成工程,但是实际情况中我们需要其他人来协作开发,此时就可以把本地仓库同步到远程仓库,同时还增加了本地仓库的一个备份.常用的远程仓库就是g ...

  5. 学科知识图谱学习平台项目 :技术栈Java、Neo4j、MySQL等超详细教学

    学科知识图谱学习平台项目 :技术栈Java.Neo4j.MySQL等超详细教学 0.效果展示 1.安装教程 安装Java SDK 11,下载前需要登录Oracle账号,下载链接,安装教程,测试是否能在 ...

  6. 【3】VSCode 主题设置推荐,自定义配色方案,修改注释高亮颜色

    相关文章: [一]tensorflow安装.常用python镜像源.tensorflow 深度学习强化学习教学 [二]tensorflow调试报错.tensorflow 深度学习强化学习教学 [三]t ...

  7. C/C++ 监控磁盘与目录操作

    遍历磁盘容量: #include <stdio.h> #include <Windows.h> void GetDrivesType(const char* lpRootPat ...

  8. 解决Maven项目创建过慢问题

    在创建时弹出Proerties位置添加键值对: archetypeCatalog internal idea 常用快捷键 Alt+回车 导入包,自动修正 Ctrl+N 查找类 Ctrl+Shift+N ...

  9. Redis主从配置、数据持久化、集群

    发布订阅 ## subscribe 订阅一个或者多个频道 ## publish 给指定的频道发送消息 ## psubscribe 订阅指定模式的频道,*代表所有 ## pubsub channels ...

  10. 谁更适合搭配甜点显卡?i7-13700KF、锐龙7 7800X3D对比:游戏相当 生产力Intel强了50%

    一.前言:如果搭配2000元甜点显卡 i7-13700KF和锐龙7 7800X3D谁更有性价比? 现在AMD最受欢迎的处理器无疑是拥有96MB三级缓存的锐龙7 7800X3D,这是一颗专为游戏而生的处 ...