ActionResult 扩展不适用于 Page() ActionResult 方法

Posted

技术标签:

【中文标题】ActionResult 扩展不适用于 Page() ActionResult 方法【英文标题】:ActionResult extension doesn't work with Page() ActionResult method 【发布时间】:2019-09-23 03:29:34 【问题描述】:

我有一个 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():

【问题讨论】:

那么InnerResult 为空。验证传递给结果的类型。很有可能是问题 我也建议,因为没有理由等待您删除异步的任何内容,而只需在 InformMessageResult 中删除 return InnerResult.ExecuteResultAsync(context); @Nkosi -- 我添加了正在传递的内容的屏幕截图。看起来它获得了一个值,但其中的所有内容都是空的。这就是 Page() 通常的工作方式吗? Page()方法的情况下,您的扩展可能在框架有时间在执行前对其执行一些相关功能之前在结果对象上执行。 我不知道,这比我习惯的要多一点。我唯一能想到的是 Page 可能会返回已经存在的请求,因此发送的 ActionResult 为 null 而使用 Redirect,它会创建一个新请求? 【参考方案1】:

这是一个较老的问题,但我自己需要这样的功能,并深入挖掘以找到原因。

从调试中可以看出,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;
    

【讨论】:

谢谢!工作一种享受。允许我使用剃须刀页面进行此操作:trycatchfail.com/2018/01/22/… 绝对是我需要的,谢谢【参考方案2】:

我怀疑的问题是 Page() 和 RedirectToPage() 继承自不同的基类。

RedirectToPage() 根据这个documentation。它具有以下继承:

对象 -> ActionResult -> RedirectToPageResult

这是由控制器的某些继承暴露的。所以你ActionResult的扩展名是可以使用的。

但是,Page() 方法是 RazorPages 类的一部分,根据 documentation。所以它的继承如下:1

对象 -> RazorPageBase -> PageBase -> 页面

现在该类的 Page() 方法确实返回了一个 PageResult,它看起来从 ActionResult 继承为 defined here.

因此,考虑到这一点,我建议先将其转换为基础 ActionResult,然后使用您的扩展方法。可能是这样的:

var baseClass = (Page() as ActionResult);
return baseClass.WithMessage(etc);

1您可以在 OP 提供的第二张图片中看到基本类型。

【讨论】:

我试过了,但仍然得到同样的错误。我用断点对其进行了更多跟踪,似乎 InnerResult 和 context 都设置为一个实例,所以我不知道它认为什么没有设置为一个对象的实例。它所做的所有转换都是将所有空内容分配给不同的 ActionResult。这很奇怪,我不确定发生了什么。 您是否尝试过在扩展方法中放置一个断点并查看其中是否有任何未设置为实例的正在传递或使用的内容,即 null?【参考方案3】:

Page()RedirectToPage() 分别是 PageResultRedirectToPageResult 的辅助方法。因此,您无需更新,而是调用这些方法。

当您调用Page() 时,它会在后台调用ExecuteResultAsync 来渲染PageResult。此时,所有属性都为空,即PageModelViewData 等。由于结果已经呈现,因此您不能使用WithMessage 扩展方法调用另一个ExecuteResultAsync

当您调用 RedirectToPage 时,它会发出 301/302 并生成一个新的 Request,这将导致 RedirectToPageResult。所以RedirectToPageResult 还没有渲染,你可以选择使用你的WithMessage 扩展方法。

话虽如此,我认为在使用Page() 方法时不可能使用WithMessage,AFAIK。

【讨论】:

以上是关于ActionResult 扩展不适用于 Page() ActionResult 方法的主要内容,如果未能解决你的问题,请参考以下文章

索赔原则发现第一个价值不适用于服务附加性

page_size 参数不适用于无云服务

图像尺寸不适用于 Page Control 和 ScrollView?

page.set('content')不适用于phantomjs中的动态内容

PHP会话不适用于多个WordPress页面

CSS 不适用于 CodeIgniter