简体   繁体   中英

await within OnActionExecutedAsync on ActionFilterAttribute

I am having difficulty overriding the OnActionExecutedAsync method of the ActionFilterAttribute in a WebApi project. So far I have the following c# code

public class MyFilter : ActionFilterAttribute
{
    public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionContext, CancellationToken cancellationToken)
    {
        var response = await MyAsyncMethod();
        if(response.SomeProperty)
            DoThing();
    }
}

However when debugging the code, the response to the await method never returns. This is true for any async method as I have tested a few I know to be working elsewhere in my code base. I tried void methods too, as well as using .Wait() and .Result all having the same issue.

var response = MyAsyncMethod().Result;
await MyVoidAsyncMethod();
MyVoidAsyncMethod().Wait(cancellationToken);

So I think the issue is with awaiting any method inside the OnActionExecutedAsync method.

I noticed I can await the base method without an issue though.

await base.OnActionExecutedAsync(actionContext, cancellationToken);

How can I call an async method inside the OnActionExecutedAsync method?

Update with example showing purely awaited methods

As a resolution in the comments was suggested by making sure all methods in the chain are awaited I have added an example that shows only awaited methods that still causes the issue.

public class MyFilter : ActionFilterAttribute
{
    public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionContext, CancellationToken cancellationToken)
    {
        await base.OnActionExecutedAsync(actionContext, cancellationToken);
        await DefinitelyAllAsync();
    }

    private async Task DefinitelyAllAsync()
    {
        var request = WebRequest.Create("http://www.stackoverflow.com");
        var response = await Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null) as HttpWebResponse;
        Debug.Assert(response?.StatusCode == HttpStatusCode.OK);
    }
}

This never reaches the Debug.Assert .

The issue was that another filter acting on the request at an earlier point was not handling async properly. In the below example, the AuthFilter will act on the request earlier in it's lifecycle than the ActionFilter , and if it doesn't implement async correctly then the thread will have issues with async when it hits other filters later in the request lifecycle. My mistake was assuming that the ActionFilter was running on it's own thread at that it was the entry point for my code. The following code shows both filters correctly implementing the await operator.

Global.asax.cs

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
    GlobalConfiguration.Configuration.Filters.Add(new AuthFilter());
    GlobalConfiguration.Configuration.Filters.Add(new ActionFilter());
}

AuthFilter.cs

public class AuthFilter : IAuthorizationFilter
{
    public bool AllowMultiple => false;
    public async Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        // incorrect use of async in this Filter will break async in future filters
        return await continuation();
    }
}

ActionFilter.cs

public class ActionFilter : ActionFilterAttribute
{
    public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionContext, CancellationToken cancellationToken)
    {
        await base.OnActionExecutedAsync(actionContext, cancellationToken);
        await GetStackoverflow();
    }

    private async Task GetStackoverflow()
    {
        var request = WebRequest.Create("http://www.stackoverflow.com");
        var response = await Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null) as HttpWebResponse;
        // this debug is unreachable if a past filter has implemented async incorrectly
        Debug.Assert(response?.StatusCode == HttpStatusCode.OK);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM