Async Controller Action with Umbraco 7 returns string

淺唱寂寞╮ 提交于 2019-11-30 12:59:50

The SurfaceControllers in Umbraco ultimately derive from System.Web.Mvc.Controller However they have custom action invoker (RenderActionInvoker) set.

RenderActionInvoker inherits from ContollerActionInvoker. In order to process async actions it should instead derive from AsyncContolkerActionInvoker. RenderActionInvoker overrides only the findaction method so changing to derive from AsyncContolkerActionInvoker is easy.

Once I recompiled Umbraco.Web with this change, async actions worked fine.

Rather than recompiling the whole project, I guess you could specify a new actioninvoker on each class

public class RenderActionInvokerAsync : System.Web.Mvc.Async.AsyncControllerActionInvoker
{

    protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
    {
        var ad = base.FindAction(controllerContext, controllerDescriptor, actionName);

        if (ad == null)
        {
            //check if the controller is an instance of IRenderMvcController
            if (controllerContext.Controller is IRenderMvcController)
            {
                return new ReflectedActionDescriptor(
                    controllerContext.Controller.GetType().GetMethods()
                        .First(x => x.Name == "Index" &&
                                    x.GetCustomAttributes(typeof(NonActionAttribute), false).Any() == false),
                    "Index",
                    controllerDescriptor);

            }
        }
        return ad;
    }

}

public class TestController : SurfaceController
{

    public TestController() {
        this.ActionInvoker = new RenderActionInvokerAsync();
    }

    public async Task<ActionResult> Test()
    {
        await Task.Delay(10000);
        return PartialView("TestPartial");

    }
}

Haven't tested this way of doing things though.

Just FYI I've added an issue to the tracker for this: http://issues.umbraco.org/issue/U4-5208

There is a work around though:

Create a custom async render action invoke (as per above):

public class FixedAsyncRenderActionInvoker : System.Web.Mvc.Async.AsyncControllerActionInvoker
{
    protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
    {
        var ad = base.FindAction(controllerContext, controllerDescriptor, actionName);

        if (ad == null)
        {
            //check if the controller is an instance of IRenderMvcController
            if (controllerContext.Controller is IRenderMvcController)
            {
                return new ReflectedActionDescriptor(
                    controllerContext.Controller.GetType().GetMethods()
                        .First(x => x.Name == "Index" &&
                                    x.GetCustomAttributes(typeof(NonActionAttribute), false).Any() == false),
                    "Index",
                    controllerDescriptor);

            }
        }
        return ad;
    }

}

Create a custom render mvc controller:

public class FixedAsyncRenderMvcController : RenderMvcController
{
    public FixedAsyncRenderMvcController()
    {
        this.ActionInvoker = new FixedAsyncRenderActionInvoker();
    }
}

Create a custom render controller factory:

public class FixedAsyncRenderControllerFactory : RenderControllerFactory
{
    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controller1 = base.CreateController(requestContext, controllerName);
        var controller2 = controller1 as Controller;
        if (controller2 != null)
            controller2.ActionInvoker = new FixedAsyncRenderActionInvoker();
        return controller1;
    }
}

Create an umbraco startup handler and replace the necessary parts with the above custom parts:

public class UmbracoStartupHandler : ApplicationEventHandler
{
    protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        DefaultRenderMvcControllerResolver.Current.SetDefaultControllerType(typeof(FixedAsyncRenderMvcController));

        FilteredControllerFactoriesResolver.Current.RemoveType<RenderControllerFactory>();
        FilteredControllerFactoriesResolver.Current.AddType<FixedAsyncRenderControllerFactory>();

        base.ApplicationStarting(umbracoApplication, applicationContext);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!