ASP.NET Core MVC应用模型的构建[4]: Action的选择
ControllerModel类型的Actions属性包含一组描述有效Action方法的ActionModel对象。对于定义在Controller类型中的所有方法,究竟哪些方法才能成为有效的Action方法呢?所以在正式介绍ActionModel类型之前,我们先来聊聊Action方法的选择规则。
一、Action方法的选择
二、ActionModel
三、PropertyModel与ParameterModel
四、SelectorModel
五、实例演示:选择器模型的构建
一、 Action方法的选择
当DefaultApplicationModelProvider对象根据某个具体的Controller类型创建对应ControllerModel对象的时候,它会提取出定义在该类型中的方法,并按照预定义的规则选择出有效的Action方法。有效的Action方法必须满足如下条件:
- 公共方法:私有(Private)、内部(Internal)和受保护(Protected)方法均为无效Action方法。
- 非抽象方法:抽象方法为无效的Action方法(这个限制可以忽略,因为Controller不会是抽象类型)。
- 非泛型方法:Action方法不能定义成泛型方法。
- 非静态方法:静态方法为无效Action方法。
- 不是从Object类型上继承的方法:Action方法支持继承,但是从Object类型上继承的方法不能成为Action方法。
- 不是对IDisposable接口的实现:如果Controller类型实现了IDisposable接口,实现的Dispose方法不是有效的Action方法。
二、ActionModel
如下面的代码片段所示,ActionModel类型实现了ICommonModel、IFilterModel和IApiExplorerModel三个接口。默认注册的DefaultApplicationModelProvider会对ActionModel对象做如下的设置:MemberInfo和ActionMethod属性都将设置为描述当前Action方法的MethodInfo对象。通过标注的特性注册到Action方法上的过滤器会被提取出来,对应的元数据会添加到Filters属性中。ApiExplorer属性返回的ApiExplorerModel对象由标注在Action方法上实现了IApiDescriptionGroupNameProvider和IApiDescriptionVisibilityProvider接口的特性构建而成。
public class ActionModel : ICommonModel, IFilterModel, IApiExplorerModel
{
public ControllerModel Controller { get; set; } public IList<IFilterMetadata> Filters { get; }
public ApiExplorerModel ApiExplorer { get; set; }
public IDictionary<object, object> Properties { get; } public IList<ParameterModel> Parameters { get; }
public IList<SelectorModel> Selectors { get; } public MethodInfo ActionMethod { get; }
public string DisplayName { get; }
public string ActionName { get; set; }
public IReadOnlyList<object> Attributes { get; } public IOutboundParameterTransformer RouteParameterTransformer { get; set; }
public IDictionary<string, string> RouteValues { get; } MemberInfo ICommonModel.MemberInfo { get; }
string ICommonModel.Name { get; }
}
DefaultApplicationModelProvider会为Action方法的每个参数创建一个ParameterModel对象,并添加到Parameters属性中。应用在Action方法上的用于封装路由信息(特性路由、约束和终结点元数据)的SelectorModel对象会按照上述的方式构建出来,并添加到Selectors属性中。标注在Action方法上的特性会被提取出来并添加到Attributes属性返回的列表中。表示Action名称的ActionName与Name属性具有相同的值,DefaultApplicationModelProvider会默认将它们设置为方法的名称,但是我们可以在方法上通过标注如下这个ActionNameAttribute特性对该属性进行设置。
[AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]
public sealed class ActionNameAttribute : Attribute
{
public string Name { get; }
public ActionNameAttribute(string name);
}
我们照例通过一个简单的示例来演示一下DefaultApplicationModelProvider对象针对ActionModel的构建规则。我们依然沿用前面创建的演示实例,这次我们将FoobarController定义成如下的形式。如代码片段所示,我们为Action方法定义了三个参数,并在上面标注了四个特性,其中FoobarAttribute特性是我们应用在该Action方法上的过滤器,ApiExplorerSettingsAttribute特性针对ApiExplorer作了相应设置,HttpGetAttribute特性完成了针对路由和HTTP方法约束的定义,ActionNameAttribute特性则将Action名称设置为“Baz”。
public class FoobarController
{
[Foobar]
[ApiExplorerSettings(GroupName ="test")]
[HttpGet("/foobar/baz")]
[ActionName("Baz")]
public void Index(string foo, string bar, string baz) => throw new NotImplementedException();
}
我们需要修改定义在HomeController中的Action方法Index。如下面的代码片段所示,在利用通过参数注入的ApplicationModelProducer对象根据提供的FoobarController类型创建出对应ApplicationModel对象之后,我们提取出描述Action方法Index的ActionModel对象,并将其作为Model呈现在默认的View中。
public class HomeController : Controller
{
[HttpGet("/")]
public IActionResult Index([FromServices]ApplicationModelProducer producer)
{
var applicationModel = producer.Create(typeof(FoobarController));
return View(applicationModel.Controllers.Single().Actions.Single());
}
}
我们将按照如下的形式重新定义了Action方法Index对应的View。如下面的代码片段所示,这是一个Model类型为ActionModel的强类型View,,它将ActionModel承载的元数据呈现在一个表格中。
@model Microsoft.AspNetCore.Mvc.ApplicationModels.ActionModel
@{
var filters = Model.Filters;
var routeValues = Model.RouteValues.ToArray();
var parameters = Model.Parameters;
var attributes = Model.Attributes;
}
<html>
<head>
<title>Action</title>
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0">
<tr><td>Method</td><td>@Model.ActionMethod.Name</td></tr>
<tr><td>ActionName</td><td>@Model.ActionName</td></tr>
<tr><td>DisplayName</td><td>@Model.DisplayName</td></tr>
<tr>
<td rowspan="@parameters.Count">Parameters</td>
<td>@parameters[0].Name</td>
</tr>
@for (int index = 1; index < parameters.Count; index++)
{
<tr><td>@parameters[index].Name</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="@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>
}
@if (routeValues.Length == 0)
{
<tr><td>RouteValues</td><td>N/A</td></tr>
}
else
{
<tr>
<td rowspan="@routeValues.Length">RouteValues</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所示的输出结果。我们从图中可以看出,Action名称来源于标注在方法上的ActionNameAttribute特性。DefaultApplicationModelProvider会为方法的每个参数创建一个ParameterModel对象并添加到ActionModel对象的Properties属性中。通过特性标注注册到Action方法上的FoobarAttribute过滤器被添加到ActionModel对象的Filters属性中。Action方法标注的四个特性全部被添加到ActionModel对象的Attributes属性中。ActionModel对象的ApiExplorer属性返回的ApiExplorerModel对象是由标注在方法上的ApiExplorerSettingsAttribute特性构建的。值得一提的是,Controller和Action的名称此时并没有作为路由参数添加到RouteValues属性中。
图1 Action模型默认的构建规则
三、PropertyModel与ParameterModel
默认注册的DefaultApplicationModelProvider会将定义在Controller类型的公共属性(包括从基类继承的属性)提取创建,然后创建相应的PropertyModel对象并添加到ControllerModel对象的ControllerProperties属性中。描述属性的PropertyModel对象和描述参数的ParameterModel对象都是为了提供模型绑定的元数据,所以它们具有相同的基类ParameterModelBase。
public abstract class ParameterModelBase : IBindingModel
{
public string Name { get; protected set; }
public Type ParameterType { get; }
public IReadOnlyList<object> Attributes { get; }
public BindingInfo BindingInfo { get; set; }
public IDictionary<object, object> Properties { get; }
}
如上面的代码片段所示,抽象类ParameterModelBase实现了IBindingModel,所以它需要利用实现的BindingInfo书信提供模型绑定信息。ParameterModelBase并没有实现IPropertyModel接口,但是其自身提供了一个Properties属性。ParameterModelBase的Name和ParameterType属性分别表示对应参数/属性的名称和类型。标注到属性上的特性会添加到Attributes属性中。如下所示的是描述Controller属性的PropertyModel类型,和描述Action方法参数的ParameterModel类型的定义。
public class PropertyModel : ParameterModelBase, ICommonModel, IBindingModel
{
public ControllerModel Controller { get; set; }
public PropertyInfo PropertyInfo { get; }
public string PropertyName { get; set; }
public IReadOnlyList<object> Attributes { get; }
public IDictionary<object, object> Properties { get; }
MemberInfo ICommonModel.MemberInfo { get; }
} public class ParameterModel : ParameterModelBase, ICommonModel
{
public ActionModel Action { get; set; }
public ParameterInfo ParameterInfo { get; }
public string ParameterName { get; set; }
public string DisplayName { get; }
public IReadOnlyList<object> Attributes { get; }
public IDictionary<object, object> Properties { get; }
MemberInfo ICommonModel.MemberInfo { get; }
}
四、SelectorModel
SelectorModel类型是对Action选择器(Selector)的描述,这里的选择器旨在解决如何为请求选择匹配Action的问题,所以它承载的其实针对路由的原始定义。如下面的代码片段所示,SelectorModel类型通过AttributeRouteModel、ActionConstraints和EndpointMetadata分别存储了特性路由信息、约束和终结点元数据。
public class SelectorModel
{
public AttributeRouteModel AttributeRouteModel { get; set; }
public IList<IActionConstraintMetadata> ActionConstraints { get; }
public IList<object> EndpointMetadata { get; }
} public class AttributeRouteModel
{
public IRouteTemplateProvider Attribute { get; }
public string Template { get; set; }
public int? Order { get; set; }
public string Name { get; set; }
public bool SuppressLinkGeneration { get; set; }
public bool SuppressPathMatching { get; set; }
public bool IsAbsoluteTemplate { get; }
}
由于路由可以通过标注到Controller类型或者Action方法上相应的特性来定义,所以描述Controller类型和Action方法的ControllerModel和ActionModel类型都具有一个Selectors属性来保存各自的选择器,DefaultApplicationModelProvider针对它们的解析方式也是一致的。这里用来定义路由的特性实现了如下所示的IRouteTemplateProvider特性。
public interface IRouteTemplateProvider
{
string Template { get; }
int? Order { get; }
string Name { get; }
}
顾名思义,实现IRouteTemplateProvider接口的特性旨在定义一个针对指定模板的路由。除此之外,针对SelectorModel的构建还涉及另一个名为IActionHttpMethodProvider的接口,实现该接口的特性为目标Action定义针对HTTP方法的约束。
public interface IActionHttpMethodProvider
{
IEnumerable<string> HttpMethods { get; }
}
对于目前提供的实现了IActionHttpMethodProvider接口的特性来说,它们无一例外都同时实现了IRouteTemplateProvider接口。比如下面这个AcceptVerbsAttribute特性可以标注到Action方法上指定一组支持的HTTP方法,同时也可以利用三个属性(Route、Name和Order)对路由作相应的定义(显式实现的Template和Order属性与自身Route和Order属性具有相同的值)。顺便提一下,我们在AcceptVerbsAttribute特性构造函数以字符串指定的HTTP方法名称会一律转换成大写形式。
[AttributeUsage( AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
public sealed class AcceptVerbsAttribute : Attribute, IActionHttpMethodProvider, IRouteTemplateProvider
{
public IEnumerable<string> HttpMethods { get; }
public string Route { get; set; }
public int Order { get; set; }
public string Name { get; set; } string IRouteTemplateProvider.Template { get; }
int? IRouteTemplateProvider.Order { get; } public AcceptVerbsAttribute(string method);
public AcceptVerbsAttribute(params string[] methods);
}
虽然AcceptVerbsAttribute特性可以为我们指定多个支持的HTTP方法,但是我们似乎更倾向于使用针对具有某种HTTP方法的特性,比如HttpGetAttribute、HttpPostAttribute、HttpPutAttribute、HttpDeleteAttribute、HttpHeadAttribute、HttpPatchAttribute和HttpOptionsAttribute特性,它们都派生于如下这个抽象的HttpMethodAttribute特性类型。
[AttributeUsage( AttributeTargets.Method, AllowMultiple=true, Inherited=true)]
public abstract class HttpMethodAttribute : Attribute, IActionHttpMethodProvider, IRouteTemplateProvider
{
public IEnumerable<string> HttpMethods { get; }
public string Template { get; }
public int Order { get; set; }
public string Name { get; set; } int? IRouteTemplateProvider.Order { get; } public HttpMethodAttribute(IEnumerable<string> httpMethods);
public HttpMethodAttribute(IEnumerable<string> httpMethods, string template);
}
IRouteTemplateProvider接口除了上述这些实现类型之外,如下这个专门用来定义路由的RouteAttribute特性单独实现了该接口。从提供的代码片段可以看出,RouteAttribute特性可以标注在Controller类型或者具体的Action方法上。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=true, Inherited=true)]
public class RouteAttribute : Attribute, IRouteTemplateProvider
{
public string Template { get; }
public int Order { get; set; }
public string Name { get; set; } public RouteAttribute(string template);
int? IRouteTemplateProvider.Order { get; }
}
DefaultApplicationModelProvider针对SelectorModel对象的构建逻辑分为两种情况。如果标注在当前Controller类型或者Action方法上的特性没有提供任何路由信息,这种情况由分为两种场景:其一,根本没有标注任何实现了IRouteTemplateProvider接口的特性;其二,所有的特性并没有对定义在该接口的三个属性(Template、Name和Order)做任何设置,比如我们经常在Action方法上标注一个没有提供任何参数的HttpGetAttribute特性,其目的只是限制支持的HTTP方法,而不是定义路由。
在这种情况下,DefaultApplicationModelProvider会创建一个唯一的SelectorModel对象。由于没有任何路由被定义,所以该对象的AttributeRouteModel属性会返回Null。标注在Controller类型或者Action方法上实现了IActionConstraintMetadata接口的特性会作为约束添加到ActionConstraints属性中。如果标注了实现IActionHttpMethodProvider接口的特性对HTTP方法做了限制,一个对应的HttpMethodActionConstraint对象会额外创建出来并添加到ActionConstraints属性中。与此同时,一个针对HTTP方法列表的HttpMethodMetada对象会被创建出来并作为终结点元数据被添加到EndpointMetadata属性中。除此之外,所有特性都会作为终结点元数据添加到EndpointMetadata属性中。
如果当前Controller类型或者Action方法上标注了实现IRouteTemplateProvider接口的特性,并且作了具体的路由设置,DefaultApplicationModelProvider会为每个IRouteTemplateProvider对象创建一个SelectorModel对象。对于每一个针对IRouteTemplateProvider对象创建的SelectorModel对象来说,设置的路由信息会被提取出来用于创建对应的AttributeRouteModel对象。如果当前特性是一个IActionHttpMethodProvider对象,一个对应的HttpMethodActionConstraint对象会额外创建出来并添加到ActionConstraints属性中。与此同时一个针对HTTP方法列表的HttpMethodMetada对象会被创建出来,当前特性和这个对象都将作为终结点元数据被添加到EndpointMetadata属性中。
如果当前IRouteTemplateProvider对象类型为RouteAttribute,那些没有提供路由信息的实现了IActionHttpMethodProvider接口的特性(即只定义了HTTP方法约束的特性)会被提取出来,一个根据它们提供的HTTP方法列表创建的HttpMethodActionConstraint对象并添加到ActionConstraints属性中。与此同时,一个针对HTTP方法列表的HttpMethodMetada对象会被创建出来并作为终结点元数据被添加到EndpointMetadata属性中。EndpointMetadata属性最终包含的终结点元数据还包括当前RouteAttribute特性和这些单纯定义约束的特性。
五、实例演示:选择器模型的构建
对于DefaultApplicationModelProvider为Controller类型或者Action方法构建SelectorModel的逻辑,我想针对具体的场景会更好理解一点,为此我们来演示几个简单的实例。我们依然沿用前面的演示程序,并将FoobarController类型改写成如下的形式。FoobarController类型中定义了三个Action方法,接下来我们看看DefaultApplicationModelProvider对象会为它们创建出怎样的选择器。
public class FoobarController
{
[HttpGet]
[HttpPost]
public void Foo()=>throw new NotImplementedException(); [HttpGet("bar")]
[HttpPost("bar")]
public void Bar() => throw new NotImplementedException(); [HttpGet()]
[HttpPost("bar")]
[Route("bar")]
public void Baz() => throw new NotImplementedException();
}
我们对定义在HomeController中的Action方法Index作如下的修改。如代码片段所示,在该方法中,我们利用通过参数注入的ApplicationModelProducer对象根据FoobarController类型创建出对应的ApplicationModel对象,然后利用查询字符串绑定的actionName参数提取出描述对应Action的ActionModel对象。我们将ActionModel对象的Selectors属性提取的选择器列表作为Model呈现在View中。
public class HomeController : Controller
{
[HttpGet("/")]
public IActionResult Index([FromServices]ApplicationModelProducer producer, [FromQuery]string actionName)
{
var applicationModel = producer.Create(typeof(FoobarController));
var actionModel = applicationModel.Controllers.Single().Actions.Single(it=>string.Compare(it.ActionName, actionName, true) == 0);
return View(actionModel.Selectors);
}
}
如下所示的是修改后的View。如代码片段所示,这个是一个Model类型为IList<SelectorModel>的强类型View,我们将提供的用于描述选择器的每个SelectorModel对象的元数据信息呈现在表格中。
@using Microsoft.AspNetCore.Mvc.ApplicationModels;
@model IList<SelectorModel>
<html>
<head>
<title>Selectors</title>
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0">
@for (int i = 0; i < Model.Count; i++)
{
var selector = Model[i];
var constraints = selector.ActionConstraints;
var metadata = selector.EndpointMetadata;
<tr><td colspan="2">Selector @(i+1)</td></tr>
<tr>
<td>AttributeRouteModel</td>
<td>@selector.AttributeRouteModel?.Template</td>
</tr>
<tr>
<td rowspan="@constraints.Count">ActionConstraints</td>
<td>@constraints[0].GetType().Name</td>
</tr>
@for (int j = 1; j < constraints.Count; j++)
{
<tr><td>@constraints[j].GetType().Name</td></tr>
}
<tr>
<td rowspan="@metadata.Count">EndpointMetadata</td>
<td>@metadata[0].GetType().Name</td>
</tr>
@for (int j = 1; j < metadata.Count; j++)
{
<tr><td>@metadata[j].GetType().Name</td></tr>
}
}
</table>
</body>
</html>
由于第一个Action方法Foo上的两个IActionHttpMethodProvider特性并没有提供任何的路由信息,所以它只具有一个AttributeRouteModel属性为Null的SelectorModel对象。这两个特性提供的针对HTTP方法(GET和POST)的约束会转换成一个HttpMethodActionConstraint对象并添加到SelectorModel对象的ActionConstraints属性中。除此之外,这两个特性会直接作为终结点元数据被添加到SelectorModel对象的EndpointMetadata属性中,该属性还会包含一个针对HTTP方法约束的HttpMethodMetada对象。图2所示的就是演示应用返回的针对Action方法Foo的选择器信息。
图2 Action方法Foo的选择器
第二个Action方法Bar上的两个特性均指定了路由模板,所以DefaultApplicationModelProvider会为它创建两个针对性的SelectorModel对象。DefaultApplicationModelProvider会根据特性(HttpGetAttribute和HttpPostAttribute)提供的路由信息来创建对应的AttributeRouteModel对象。SelectorModel对象ActionConstraints属性会包含根据各自提供的HTTP方法约束创建的HttpMethodActionConstraint对象。EndpointMetadata属性将会包含两个终结点元数据对象,分别是当前的特性和根据HTTP方法约束创建的HttpMethodMetada对象。图3所示的就是演示应用返回的针对Action方法Bar的选择器列表。
图3 Action方法Bar的选择器
第三个Action方法方法上标注了三个特性,但是其中只有两个特性提供了路由信息,所以DefaultApplicationModelProvider最终会根据标注的HttpPostAttribute和RouteAttribute特性创建出两个对应的SelectorModel对象。根据标注的HttpPostAttribute特性针对SelectorModel对象的创建与上面一致,所以我们现在只关注针对RouteAttribute特性创建的SelectorModel对象。该对象提供的AttributeRouteModel对象自然由RouteAttribute特性提供的路由信息来创建。
该方法上没有提供路由信息的HttpGetAttribute特性将被用来提供当前路由的约束,所以这个SelectorModel对象的ActionConstraints属性中会包含一个根据这个特性创建的HttpMethodActionConstraint对象。这个SelectorModel对象的EndpointMetadata属性中最终会包含三个终结点元数据,分别是标注的RouteAttribute和HttpGetAttribute特性,以及根据HTTP方法约束创建的HttpMethodMetada对象。图4所示的就是演示应用返回的针对Action方法Baz的选择器列表。
图4 Action方法Bar的选择器
ASP.NET Core MVC应用模型的构建[4]: Action的选择的更多相关文章
- ASP.NET Core MVC/WebAPi 模型绑定探索
前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...
- ASP.NET Core MVC 之模型(Model)
1.模型绑定 ASP.NET Core MVC 中的模型绑定将数据从HTTP请求映射到操作方法参数.参数既可以是简单类型,也可以是复杂类型.MVC 通过抽象绑定解决了这个问题. 2.使用模型绑定 当 ...
- ASP.NET Core MVC/WebAPi 模型绑定探索 转载https://www.cnblogs.com/CreateMyself/p/6246977.html
前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...
- 【转】ASP.NET Core MVC/WebAPi 模型绑定探索
前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...
- ASP.NET MVC和ASP.NET Core MVC中获取当前URL/Controller/Action (转载)
ASP.NET MVC 一.获取URL(ASP.NET通用): [1]获取完整url(协议名+域名+虚拟目录名+文件名+参数) string url=Request.Url.ToString(); [ ...
- ASP.NET Core MVC/WebAPi 模型绑定
public class Person { public string Name { get; set; } public string Address { get; set; } public in ...
- ASP.NET Core MVC 模型绑定用法及原理
前言 查询了一下关于 MVC 中的模型绑定,大部分都是关于如何使用的,以及模型绑定过程中的一些用法和概念,很少有关于模型绑定的内部机制实现的文章,本文就来讲解一下在 ASP.NET Core MVC ...
- Pro ASP.NET Core MVC 第6版 第一章
目录 第一章 ASP.NET Core MVC 的前世今生 ASP.NET Core MVC 是一个微软公司开发的Web应用程序开发框架,它结合了MVC架构的高效性和简洁性,敏捷开发的思想和技术和.N ...
- ASP.NET Core MVC学习笔记
最近由于疫情紧张,遂在家办公,在领导的带领下,学习了一下.Net Core MVC. 一,构建web应用 1.选择c#-所有平台-web 找到ASP.NET Core web应用程序 2.项目命名之 ...
- ASP.NET Core MVC中URL和数据模型的匹配
Http GET方法 首先我们来看看GET方法的Http请求,URL参数和ASP.NET Core MVC中Controller的Action方法参数匹配情况. 我定义一个UserController ...
随机推荐
- [转帖]CPU Utilization is Wrong
Brendan Gregg's Blog home CPU Utilization is Wrong 09 May 2017 The metric we all use for CPU utiliza ...
- 文心一言 VS 讯飞星火 VS chatgpt (187)-- 算法导论14.1 4题
四.用go语言,写出一个递归过程 OS-KEY-RANK(T,k),以一棵顺序统计树T和一个关键字k作为输入,要求返回 k 在由 T 表示的动态集合中的秩.假设 T 的所有关键字都不相同. 文心一言, ...
- STM32CubeMX教程25 PWR 电源管理 - 睡眠、停止和待机模式
1.准备材料 开发板(正点原子stm32f407探索者开发板V2.4) STM32CubeMX软件(Version 6.10.0) 野火DAP仿真器 keil µVision5 IDE(MDK-Arm ...
- vite配置开发环境和生产环境
为什么需要境变量的配置 在很多的时候,我们会遇见这样的问题. 开发环境的接口是:http://test.com/api 但是我们的生产环境地址是:http://yun.com/api 此时,我们打包的 ...
- 9.2 Windows驱动开发:内核解析PE结构导出表
在笔者的上一篇文章<内核特征码扫描PE代码段>中LyShark带大家通过封装好的LySharkToolsUtilKernelBase函数实现了动态获取内核模块基址,并通过ntimage.h ...
- C/C++ 反汇编:针对加减乘除的还原
算术运算通常是指,加减乘除四则运算,而计算机中的四则运算与数学中的有所不同,同样是实现算术运算,高级语言与汇编语言的实现思路完全不同,往往一个简单的减法运算,都要几条指令的配合才能得出计算结果,而为了 ...
- Socket.D v2.3.9 发布(增加 node.js server 实现)
Socket.D 是基于"事件"和"语义消息""流"的网络应用层传输协议.有用户说,"Socket.D 之于 Socket,尤如 ...
- 答疑解惑:解释在Mybatis-Spring下@Mapper与@MapperScan为何不能同时生效以及实现动态条件注册Mapper接口
若项目中使用了@MapperScan注解后,则@Mapper注解不再生效, 原因是:@MapperScan注解 会执行@Import(MapperScannerRegistrar.class),而Ma ...
- TActionManager Delphi 超级方便的快捷键 草草
delphi 中用快捷键 草 实在是 太简单了 . 自己摸索出来的 方法 --------------------------------------------------------------- ...
- 《ASP.NET Core 与 RESTful API 开发实战》-- (第8章)-- 读书笔记(上)
第 8 章 认证和安全 8.1 认证 认证(Authentication)是指验证用户身份的过程,授权(Authorization)是验证一个已经通过认证的用户是否有权限做某些事的过程 常见的 HTT ...