ActionResult 扩展不适用于 Page() ActionResult 方法
ActionResult extension doesn't work with Page() ActionResult method
我有一个 ActionResult 扩展,它在返回页面时向 TempData 添加了 toast:
public static IActionResult WithMessage(this ActionResult result, InformMessage msg)
{
return new InformMessageResult(result, msg);
}
这是 InformMessageResult:
public class InformMessageResult : ActionResult
{
public ActionResult InnerResult { get; set; }
public InformMessage InformMessage { get; set; }
public InformMessageResult (ActionResult innerResult, InformMessage informMsg)
{
InnerResult = innerResult;
InformMessage = informMsg;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
ITempDataDictionaryFactory factory = context.HttpContext.RequestServices.GetService(typeof(ITempDataDictionaryFactory)) as ITempDataDictionaryFactory;
ITempDataDictionary tempData = factory.GetTempData(context.HttpContext);
tempData.Put("InformMessage", InformMessage);
await InnerResult.ExecuteResultAsync(context);
}
}
这适用于
return RedirectToPage(etc).WithMessage(etc)
等,但因
而失败
return Page().WithMessage(etc)
调试器突出显示
await InnerResult.ExecuteResultAsync(context);
表示 InnerResult 未设置为对象的实例。
有什么方法可以让 Return Page() 工作吗?
编辑以获取更多信息:
我测试了作为 "InnerResult" 发送的内容,它看起来像 Return Page(),一切都是空的(我想说是设计的,因为我什么也没做在那之前):
使用 RedirectToPage():
使用 Page():
我怀疑的问题是 Page() 和 RedirectToPage() 继承自不同的基 classes。
RedirectToPage() 根据此 documentation。它具有以下继承性:
Object -> ActionResult -> RedirectToPageResult
这是通过控制器的一些继承暴露出来的。因此,您可以使用 ActionResult
的扩展。
但是,根据此 documentation,Page() 方法是 RazorPages class 的一部分。所以它的继承方式如下:1
Object -> RazorPageBase -> PageBase -> Page
现在 class 的 Page() 方法执行 return 一个 PageResult
看起来从 ActionResult
继承为 defined here.
考虑到这一点,我建议先将其转换为基础 ActionResult
,然后再使用您的扩展方法。可能是这样的:
var baseClass = (Page() as ActionResult);
return baseClass.WithMessage(etc);
1 您可以在 OP 提供的第二张图片中看到基本类型。
Page()
和 RedirectToPage()
分别是 PageResult
和 RedirectToPageResult
的辅助方法。因此,您可以调用这些方法而不是更新。
当您调用 Page()
时,它会在幕后调用 ExecuteResultAsync
来渲染 PageResult
。那时,所有属性都为空,即 Page
、Model
、ViewData
等。由于结果已经呈现,因此您不能调用另一个 ExecuteResultAsync
使用WithMessage
扩展方法。
当您调用 RedirectToPage
时,它会发出一个 301/302 并生成一个新的 Request
,这将导致 RedirectToPageResult
。因此 RedirectToPageResult
尚未呈现,您可以选择使用 WithMessage
扩展方法。
话虽如此,我认为在使用 Page()
方法时不可能使用 WithMessage
,AFAIK。
这是一个较旧的问题,但我自己需要这样的功能,并深入挖掘以找到原因。
正如您从调试中看到的那样,Page
方法生成了一个完全空白的 PageResult
。因为每个 属性 都是空的,调用 ExecuteResultAsync
失败,因为它显然不能对全空值做任何事情。
Page()
在其余时间正常工作的原因是 PageActionInvoker 中的幕后魔术,特别是在其 InvokeResultAsync 方法中。它会检测到您的 ViewData 和 Page 是空白的,并在它自己调用 pageResult.ExecuteResultAsync
方法之前填充它们。
因此,如果您执行与 PageActionInvoker 相同的工作,您仍然可以使 InformMessageResult
正常工作。方法如下:
public override async Task ExecuteResultAsync(ActionContext context)
{
/* temp data work goes here */
if (InnerResult is PageResult pageResult)
{
var pageContext = context as PageContext
?? throw new ArgumentException("context must be a PageContext if your InnerResult is a PageResult.", "context");
var pageFactoryProvider = pageContext.HttpContext.RequestServices.GetRequiredService<IPageFactoryProvider>();
var pageFactory = pageFactoryProvider.CreatePageFactory(pageContext.ActionDescriptor);
var viewContext = new ViewContext(
pageContext,
NullView.Instance,
pageContext.ViewData,
tempData,
TextWriter.Null,
new Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelperOptions()
);
viewContext.ExecutingFilePath = pageContext.ActionDescriptor.RelativePath;
pageResult.ViewData = viewContext.ViewData;
pageResult.Page = (PageBase)pageFactory(pageContext, viewContext);
}
await InnerResult.ExecuteResultAsync(context);
}
private class NullView : IView
{
public static readonly NullView Instance = new NullView();
public string Path => string.Empty;
public Task RenderAsync(ViewContext context)
{
if (context == null) throw new ArgumentNullException("context");
return Task.CompletedTask;
}
}
我有一个 ActionResult 扩展,它在返回页面时向 TempData 添加了 toast:
public static IActionResult WithMessage(this ActionResult result, InformMessage msg)
{
return new InformMessageResult(result, msg);
}
这是 InformMessageResult:
public class InformMessageResult : ActionResult
{
public ActionResult InnerResult { get; set; }
public InformMessage InformMessage { get; set; }
public InformMessageResult (ActionResult innerResult, InformMessage informMsg)
{
InnerResult = innerResult;
InformMessage = informMsg;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
ITempDataDictionaryFactory factory = context.HttpContext.RequestServices.GetService(typeof(ITempDataDictionaryFactory)) as ITempDataDictionaryFactory;
ITempDataDictionary tempData = factory.GetTempData(context.HttpContext);
tempData.Put("InformMessage", InformMessage);
await InnerResult.ExecuteResultAsync(context);
}
}
这适用于
return RedirectToPage(etc).WithMessage(etc)
等,但因
而失败return Page().WithMessage(etc)
调试器突出显示
await InnerResult.ExecuteResultAsync(context);
表示 InnerResult 未设置为对象的实例。
有什么方法可以让 Return Page() 工作吗?
编辑以获取更多信息:
我测试了作为 "InnerResult" 发送的内容,它看起来像 Return Page(),一切都是空的(我想说是设计的,因为我什么也没做在那之前):
使用 RedirectToPage():
使用 Page():
我怀疑的问题是 Page() 和 RedirectToPage() 继承自不同的基 classes。
RedirectToPage() 根据此 documentation。它具有以下继承性:
Object -> ActionResult -> RedirectToPageResult
这是通过控制器的一些继承暴露出来的。因此,您可以使用 ActionResult
的扩展。
但是,根据此 documentation,Page() 方法是 RazorPages class 的一部分。所以它的继承方式如下:1
Object -> RazorPageBase -> PageBase -> Page
现在 class 的 Page() 方法执行 return 一个 PageResult
看起来从 ActionResult
继承为 defined here.
考虑到这一点,我建议先将其转换为基础 ActionResult
,然后再使用您的扩展方法。可能是这样的:
var baseClass = (Page() as ActionResult);
return baseClass.WithMessage(etc);
1 您可以在 OP 提供的第二张图片中看到基本类型。
Page()
和 RedirectToPage()
分别是 PageResult
和 RedirectToPageResult
的辅助方法。因此,您可以调用这些方法而不是更新。
当您调用 Page()
时,它会在幕后调用 ExecuteResultAsync
来渲染 PageResult
。那时,所有属性都为空,即 Page
、Model
、ViewData
等。由于结果已经呈现,因此您不能调用另一个 ExecuteResultAsync
使用WithMessage
扩展方法。
当您调用 RedirectToPage
时,它会发出一个 301/302 并生成一个新的 Request
,这将导致 RedirectToPageResult
。因此 RedirectToPageResult
尚未呈现,您可以选择使用 WithMessage
扩展方法。
话虽如此,我认为在使用 Page()
方法时不可能使用 WithMessage
,AFAIK。
这是一个较旧的问题,但我自己需要这样的功能,并深入挖掘以找到原因。
正如您从调试中看到的那样,Page
方法生成了一个完全空白的 PageResult
。因为每个 属性 都是空的,调用 ExecuteResultAsync
失败,因为它显然不能对全空值做任何事情。
Page()
在其余时间正常工作的原因是 PageActionInvoker 中的幕后魔术,特别是在其 InvokeResultAsync 方法中。它会检测到您的 ViewData 和 Page 是空白的,并在它自己调用 pageResult.ExecuteResultAsync
方法之前填充它们。
因此,如果您执行与 PageActionInvoker 相同的工作,您仍然可以使 InformMessageResult
正常工作。方法如下:
public override async Task ExecuteResultAsync(ActionContext context)
{
/* temp data work goes here */
if (InnerResult is PageResult pageResult)
{
var pageContext = context as PageContext
?? throw new ArgumentException("context must be a PageContext if your InnerResult is a PageResult.", "context");
var pageFactoryProvider = pageContext.HttpContext.RequestServices.GetRequiredService<IPageFactoryProvider>();
var pageFactory = pageFactoryProvider.CreatePageFactory(pageContext.ActionDescriptor);
var viewContext = new ViewContext(
pageContext,
NullView.Instance,
pageContext.ViewData,
tempData,
TextWriter.Null,
new Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelperOptions()
);
viewContext.ExecutingFilePath = pageContext.ActionDescriptor.RelativePath;
pageResult.ViewData = viewContext.ViewData;
pageResult.Page = (PageBase)pageFactory(pageContext, viewContext);
}
await InnerResult.ExecuteResultAsync(context);
}
private class NullView : IView
{
public static readonly NullView Instance = new NullView();
public string Path => string.Empty;
public Task RenderAsync(ViewContext context)
{
if (context == null) throw new ArgumentNullException("context");
return Task.CompletedTask;
}
}