1

I would like to execute an API call via attribute before a route execution. I created a customer action filter and use the OnExecuting() override function.

Within the function I awaite an async API call to another server. However, while debugging I notice the Route executes before that async call is complete. How can I accomplish this.

    [CustomAttribute("Stuff")]
    [Route("MyRoute")]
    public async Task<IHttpActionResult> MyRoute()
    {
        await anotherAsyncFunction();
        return Ok();
    }

And the definition of the Custom Action Filter.

public class CustomAttribute : ActionFilterAttribute
{
    private string att;
    public CustomAttribute(string a)
    {
        att = a;
    }


    public async override void OnActionExecuting(HttpActionContext actionContext)
    {


        await MyFirstAsyncFunction();


    }
}

Unfortunately this doesn't finish before the route code executes.

Tyler
  • 668
  • 1
  • 8
  • 22
  • 1
    Yikes. You're blocking a call based on another service? I would suggest avoiding this route and queuing it and checking for a flag instead. You're heading down a slippery slope. -- Comment aside, you're going to need to call the API synchronously. – Brad Christie Mar 21 '16 at 13:44
  • doesn't await make it more synchronous? – Tyler Mar 21 '16 at 13:52
  • You're best bet is looking at an [`AsyncHelper`](http://stackoverflow.com/a/25097498/298053) and wrapping your call. – Brad Christie Mar 21 '16 at 14:00
  • Short answer: `async void` doesn't make the function blocking (and is a bad strategy anyways). It surrenders control once `await` is hit. True async would be `async Task` and would retain workflow as expected. However, given MVC5 [presumably your version] doesn't have an async actionfilter, you're stuck. – Brad Christie Mar 21 '16 at 14:04
  • I appreciate the help. I think I just got my problem resolved by not using await. Instead I just did HttpResponseMessage response = client.PostAsJsonAsync("/api/rotue/", res).Result; within my api call function apparently using .Result within that makes it a blocking sync call. – Tyler Mar 21 '16 at 14:08

1 Answers1

1

I created a customer action filter and use the OnExecuting() override function.

As commenters have noted, this is due to the use of async void, which does not provide an easy way for callers to wait for asynchronous operations to complete. This is one reason why my async best practices article includes the advice "avoid async void".

I would like to execute an API call via attribute before a route execution.

Unfortunately, ASP.NET MVC does not support asynchronous filters (they do, however, work just fine on ASP.NET Core). AFAIK, there are no plans to add this to the existing ASP.NET MVC product. Your options are to either execute the code manually (i.e., within the action, instead of using an attribute), or make your filter synchronous.

I just did HttpResponseMessage response = client.PostAsJsonAsync("/api/rotue/", res).Result;

This is the sync-over-async anti-pattern. It just happens to work in this case. It would be better to replace HttpClient (which is async-only) with WebClient (which supports sync).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810