在初始化时,Razor Pages Web 应用会构建一组 PageApplicationModel 实例,这些实例描述来自 Web 应用的 Razor Pages 及其关联的处理程序方法。
要了解有关其工作原理的更多信息,请查看source 中的DefaultPageApplicationModelProvider 的PopulateHandlerMethods 方法:
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 类的方法,并为每个方法调用了DefaultPageApplicationModelPartsProvider 的CreateHandlerModel 方法。 CreateHandlerModel 确定该方法是否为处理程序(例如,它是public,而不是static),然后解析方法名称以确定其 HTTP 方法、处理程序名称等。此解析发生在 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 的实例来保存提取的信息。有了这些信息,路由系统就能够根据传入的请求选择处理程序。此选择逻辑由DefaultPageHandlerMethodSelector 类处理。
DefaultPageApplicationModelPartsProvider 类实现了IPageApplicationModelPartsProvider 接口并使用 DI 进行解析。您可以创建自己的 IPageApplicationModelPartsProvider 实现并替换默认实现,例如,这将允许您执行自己的方法名称解析。
使用自定义实现,例如MyCustomPageApplicationModelPartsProvider,将类似以下内容添加到 ConfigureServices,最好是在调用 AddRazorPages 之前:
services.AddSingleton<IPageApplicationModelPartsProvider,
MyCustomPageApplicationModelPartsProvider>();
除了解释框架如何找到处理程序之外,答案还应该解释框架如何将一个文件定义为另一个文件的代码隐藏。
页面与其PageModel 之间的连接是使用.cshtml 文件中的@model 指令建立的。例如在Index.cshtml 中,您将看到@model IndexModel。您可以将Index.cshtml.cs 重命名为SomethingElse.cs,它仍然可以工作,因此文件命名更像是一种约定。