什么文件定义了代码隐藏文件中前端和方法之间的关系?
What file defines the relation between front-end and methods in the code-behind file?
当我在视图中有一个表单元素时:
create.cshtml
<form method="get">
...whatever
</form>
我们在代码隐藏文件中有一个模型:
create.cshtml.cs
public class CreateModel : PageModel
{
//...whatever
public void OnGet()
{
}
}
表单将调用 OnGet()
。我知道这是一个标准并且在大多数框架中都遵循,但这意味着它必须在 .NET 中的某个地方定义。什么文件定义了这个标准,如果可以的话,我们可以改变它吗(为了理解)?
初始化后,Razor Pages 网络应用程序构建了一个 PageApplicationModel
实例的集合,这些实例描述了网络应用程序中的 Razor 页面及其关联的处理程序方法。
要了解有关其工作原理的更多信息,请查看 DefaultPageApplicationModelProvider
的 PopulateHandlerMethods
方法的 source:
internal void PopulateHandlerMethods(PageApplicationModel pageModel)
{
var methods = pageModel.HandlerType.GetMethods();
for (var i = 0; i < methods.Length; i++)
{
var handler = _pageApplicationModelPartsProvider
.CreateHandlerModel(methods[i]);
if (handler != null)
{
pageModel.HandlerMethods.Add(handler);
}
}
}
在这里,我们可以看到框架枚举了 Razor Page 的 class 的方法并调用了 DefaultPageApplicationModelPartsProvider
的 CreateHandlerModel
method for each of these. CreateHandlerModel
determines if the method is a handler (e.g. it's public
, not static
) and then parses the method name to determine its HTTP method, handler name, etc. This parsing takes place in TryParseHandlerMethod
:
internal static bool TryParseHandlerMethod(
string methodName, out string httpMethod, out string handler)
{
httpMethod = null;
handler = null;
// Handler method names always start with "On"
if (!methodName.StartsWith("On") || methodName.Length <= "On".Length)
{
return false;
}
// Now we parse the method name according to our conventions to
// determine the required HTTP method and optional 'handler name'.
// Valid names look like:
// - OnGet
// - OnPost
// - OnFooBar
// - OnTraceAsync
// - OnPostEditAsync
var start = "On".Length;
var length = methodName.Length;
if (methodName.EndsWith("Async", StringComparison.Ordinal))
{
length -= "Async".Length;
}
if (start == length)
{
// There are no additional characters. This is "On" or "OnAsync".
return false;
}
// The http method follows "On" and is required to be at least one
// character. We use casing to determine where it ends.
var handlerNameStart = start + 1;
for (; handlerNameStart < length; handlerNameStart++)
{
if (char.IsUpper(methodName[handlerNameStart]))
{
break;
}
}
httpMethod = methodName.Substring(start, handlerNameStart - start);
// The handler name follows the http method and is optional.
// It includes everything up to the end excluding the "Async" suffix
// (if present).
handler = handlerNameStart == length
? null
: methodName.Substring(handlerNameStart, length - handlerNameStart);
return true;
}
这段代码很好地自我解释,但最终它解析出了 HTTP 方法和一个可选的处理程序名称。
最后,框架创建 PageHandlerModel
to hold the extracted information. With this information in place, the routing system is able to select a handler based on an incoming request. This selection logic gets handled by the DefaultPageHandlerMethodSelector
class.
的实例
DefaultPageApplicationModelPartsProvider
class 实现了 IPageApplicationModelPartsProvider
接口并使用 DI 解析。您可以创建自己的 IPageApplicationModelPartsProvider
实现并替换默认实现,这将允许您执行自己的方法名称解析,例如。
使用自定义实现,例如MyCustomPageApplicationModelPartsProvider
,将类似以下内容添加到 ConfigureServices
,最好是在调用 AddRazorPages
之前:
services.AddSingleton<IPageApplicationModelPartsProvider,
MyCustomPageApplicationModelPartsProvider>();
In addition to explaining how the framework finds the handlers, the answer also should explain how does the framework define one file as a code-behind for another.
页面与其 PageModel
之间的连接是使用 .cshtml 文件中的 @model
指令建立的。例如在 Index.cshtml
中,您会看到 @model IndexModel
。您可以将 Index.cshtml.cs
重命名为 SomethingElse.cs
,它仍然有效,因此文件命名更像是一种约定。
当我在视图中有一个表单元素时:
create.cshtml
<form method="get">
...whatever
</form>
我们在代码隐藏文件中有一个模型:
create.cshtml.cs
public class CreateModel : PageModel
{
//...whatever
public void OnGet()
{
}
}
表单将调用 OnGet()
。我知道这是一个标准并且在大多数框架中都遵循,但这意味着它必须在 .NET 中的某个地方定义。什么文件定义了这个标准,如果可以的话,我们可以改变它吗(为了理解)?
初始化后,Razor Pages 网络应用程序构建了一个 PageApplicationModel
实例的集合,这些实例描述了网络应用程序中的 Razor 页面及其关联的处理程序方法。
要了解有关其工作原理的更多信息,请查看 DefaultPageApplicationModelProvider
的 PopulateHandlerMethods
方法的 source:
internal void PopulateHandlerMethods(PageApplicationModel pageModel) { var methods = pageModel.HandlerType.GetMethods(); for (var i = 0; i < methods.Length; i++) { var handler = _pageApplicationModelPartsProvider .CreateHandlerModel(methods[i]); if (handler != null) { pageModel.HandlerMethods.Add(handler); } } }
在这里,我们可以看到框架枚举了 Razor Page 的 class 的方法并调用了 DefaultPageApplicationModelPartsProvider
的 CreateHandlerModel
method for each of these. CreateHandlerModel
determines if the method is a handler (e.g. it's public
, not static
) and then parses the method name to determine its HTTP method, handler name, etc. This parsing takes place in TryParseHandlerMethod
:
internal static bool TryParseHandlerMethod( string methodName, out string httpMethod, out string handler) { httpMethod = null; handler = null; // Handler method names always start with "On" if (!methodName.StartsWith("On") || methodName.Length <= "On".Length) { return false; } // Now we parse the method name according to our conventions to // determine the required HTTP method and optional 'handler name'. // Valid names look like: // - OnGet // - OnPost // - OnFooBar // - OnTraceAsync // - OnPostEditAsync var start = "On".Length; var length = methodName.Length; if (methodName.EndsWith("Async", StringComparison.Ordinal)) { length -= "Async".Length; } if (start == length) { // There are no additional characters. This is "On" or "OnAsync". return false; } // The http method follows "On" and is required to be at least one // character. We use casing to determine where it ends. var handlerNameStart = start + 1; for (; handlerNameStart < length; handlerNameStart++) { if (char.IsUpper(methodName[handlerNameStart])) { break; } } httpMethod = methodName.Substring(start, handlerNameStart - start); // The handler name follows the http method and is optional. // It includes everything up to the end excluding the "Async" suffix // (if present). handler = handlerNameStart == length ? null : methodName.Substring(handlerNameStart, length - handlerNameStart); return true; }
这段代码很好地自我解释,但最终它解析出了 HTTP 方法和一个可选的处理程序名称。
最后,框架创建 PageHandlerModel
to hold the extracted information. With this information in place, the routing system is able to select a handler based on an incoming request. This selection logic gets handled by the DefaultPageHandlerMethodSelector
class.
DefaultPageApplicationModelPartsProvider
class 实现了 IPageApplicationModelPartsProvider
接口并使用 DI 解析。您可以创建自己的 IPageApplicationModelPartsProvider
实现并替换默认实现,这将允许您执行自己的方法名称解析,例如。
使用自定义实现,例如MyCustomPageApplicationModelPartsProvider
,将类似以下内容添加到 ConfigureServices
,最好是在调用 AddRazorPages
之前:
services.AddSingleton<IPageApplicationModelPartsProvider,
MyCustomPageApplicationModelPartsProvider>();
In addition to explaining how the framework finds the handlers, the answer also should explain how does the framework define one file as a code-behind for another.
页面与其 PageModel
之间的连接是使用 .cshtml 文件中的 @model
指令建立的。例如在 Index.cshtml
中,您会看到 @model IndexModel
。您可以将 Index.cshtml.cs
重命名为 SomethingElse.cs
,它仍然有效,因此文件命名更像是一种约定。