OnExceptionAspect isn't working correctly in WebApi project

dhiren's Avatar

dhiren

07 Feb, 2018 05:07 PM

I have a WebApi controller which has a number of methods that each return a BaseResult object, which contains fields for Success and ErrorMessage. I have an OnExceptionAspect applied to the ApiController class to log all exceptions and then set the ReturnValue for the webapi method to a new BaseResult indicating failure, and including the error message.

But when I throw an exception from within the webapi, even though the exception gets logged correctly, I don't get the BaseResponse returned to my caller, but a HTTP 500 error instead. I've tried setting the AspectPriority on the exception aspect to the highest and lowest values, but it doesn't help - the exceptions always get logged, but the client gets a 500 error instead of a proper response object. I've also tried changing the method from async to synchronous, in case the task switching caused the 500 to be returned to the client before the exception handler ran, but that didn't help either.

I also have additional aspects on the controller as well, to log the requests and responses. My intention is to have the OnException aspect run before the LogResult aspect in OnMethodBoundaryAspect.OnExit, so that aside from my exception log, my request log also captures the nice error message returned to the client via the OnException aspect. The OnException aspect has priority 1, and the LogResult aspect has priority 5, so I'm expecting the exception to be handled before the OnExit, but that's not happening.

What I'm seeing is: exception thrown by code, OnExit called by LogResult (priority 5) but args.ReturnValue is currently null, HTTP 500 error received by client, OnException called by exception handler (priority 1) and sets args.ReturnValue to a proper value.
What I'd like to see is: exception thrown by code, OnException called by exception handler setting args.ReturnValue to a proper value, OnExit called by LogResult logging the ReturnValue, ReturnValue returned to client.

Controller:

    [HandleWebApiException(AspectPriority = 1)]
    [CreateApiToken(AspectPriority = 2)]
    [ValidateChannelCredentials(AspectPriority = 3)]
    [LogRequest(AspectPriority = 4)]
    [LogResponse(AspectPriority = 5)]
    [FinalizeResponse(AspectPriority = 6)]
    public class V1Controller : ApiController
    {
        public async Task<BaseResponse> QueryProducts(ApiRequest<QueryProductsRequest> request)
        {
            using (var manager = new Manager())
            {
                return new BaseResponse<QueryProductsResponse>()
                {
                    Response = await manager.QueryProductsAsync(request.Token, request.Request)
                };
            }
        }
    }
  1. 1 Posted by dhiren on 07 Feb, 2018 07:07 PM

    dhiren's Avatar

    I suspect I've managed to fix this - one of the other aspects was calling async methods so I changed the method signature from override void OnEntry to override async void OnEntry. Removing the async and reworking the internals of the aspect to run synchronously seems to have things working correctly.

    What would be nice though, would be to have an overridable public async Task OnEntryAsync(MethodExecutionArgs args) (and the rest as well), so that we can safely await inside aspects.

  2. 2 Posted by dhiren on 07 Feb, 2018 08:03 PM

    dhiren's Avatar

    It turns out I only partially fixed it. The HTTP 500 error is now gone, but I still have the out-of-order problem. The exception handler does run, and does return the appropriate error back to the client. But the LogResponse OnExit aspect is getting called before the OnException aspect. This is despite explicitly configuring the dependencies (I removed the AspectPriority directives on the ApiController):

        [PSerializable]
        [AspectTypeDependency(AspectDependencyAction.Require, AspectDependencyPosition.After, typeof(HandleWebApiExceptionAttribute))]
        [AspectTypeDependency(AspectDependencyAction.Require, AspectDependencyPosition.After, typeof(LogRequestAttribute))]
        public sealed class LogResponseAttribute : OnMethodBoundaryAspect
    

    So I want the OnException aspect to run before any others, but I still want any OnExit aspects to run if an exception has occurred.

  3. Support Staff 3 Posted by PostSharp Techn... on 08 Feb, 2018 11:03 PM

    PostSharp Technologies's Avatar

    Hello,

    I'm happy to hear that you've found a working solution for the first issue.

    The OnMethodBoundaryAspect supports async method semantics and you can find more info about this support in our docs (http://doc.postsharp.net/semantic-advising). However, you've already noticed that we don't have an API for async aspect methods and this use case is not officially supported. If you're interested in this feature, then you can add a request on our UserVoice website (https://postsharp.uservoice.com).

    In the meantime we can recommend deriving from MethodInterceptionAspect and implementing OnInvokeAsync method whenever you need to implement the advice asynchronously.

    When ordering aspects, you need to pay attention to the fact that method-boundary aspects applied to the same method are nested and OnExceptionAspect is just a special case of method-boundary aspect. This is what the order looks like when you order Aspect1 before Aspect2:

    Aspect1.OnEntry
    try
    {
        Aspect2.OnEntry
        try
        {
            OriginalMethod
        }
        catch
        {
            Aspect2.OnException
        }
        finally
        {
            Aspect2.OnExit
        }
    }
    catch
    {
        Aspect1.OnException
    }
    finally
    {
        Aspect1.OnExit
    }
    

    If you want LogResponse.OnExit to run after HandleWebApiException.OnException then you need to order LogResponse aspect before HandleWebApiException aspect.

    -alex

  4. 4 Posted by dhiren on 09 Feb, 2018 12:48 AM

    dhiren's Avatar

    Thank you Alex, that code block makes things perfectly clear now.

  5. Support Staff 5 Posted by PostSharp Techn... on 09 Feb, 2018 09:42 AM

    PostSharp Technologies's Avatar

    Hello,

    We are going to close this request as we believe it was solved. Please feel free to reopen the discussion if you need more help.

    Best regards,
    -tony

  6. PostSharp Technologies closed this discussion on 09 Feb, 2018 09:42 AM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac