Есть ли ловушка для выполнения некоторого кода в конце каждого ASP. Net вызова API контроллера? - PullRequest
0 голосов
/ 09 апреля 2020

У нас есть (внутренний) веб-сервер (IIS), написанный на C# с использованием ASP. NET. Как и большинство веб-сервисов, у нас есть сотни API, все интерфейсы «контроллера», следующие этому шаблону:

class SomeController :  System.Web.Http.ApiController
{
    [HttpGet] public HttpResponseMessage SomeGetApi(...)
    {
        try
        {
            //do stuff
            return SomeHttpResponseMessage;
        }
        finally
        {
            OurController.Cleanup();
        }
    }

    [HttpPost] public IHttpActionResult SomePostApi(...)
    {
        try
        {
            //do something
            return SomeHttpActionResult;
        }
        catch (Exception ex)
        {
            return SomeErrorHttpActionResult;
        }
        finally
        {
            OurController.Cleanup();
        }
    }
}

Единственная важная деталь здесь - это то, что весь код API обернут как выше на самом внешнем уровне в структуре try-finally, где мы должны вызывать ту же самую процедуру stati c "Cleanup ()", как и последнее, что делается при выходе из API. Другими словами, у нас есть сотни и сотни повторений этого же вызова. Каждый раз, когда кто-то добавляет новый API и забывает try-finally {cleanup ()}, все прерывается.

Очевидно, было бы неплохо, если бы ASP. Net предоставили некоторые механизм вызова некоторого (общего) кода очистки после завершения выполнения каждого API контроллера. Вот варианты, которые я рассмотрел и почему они терпят неудачу:

В ApiController имеется перезаписываемая виртуальная protected virtual void Dispose(bool disposing);. Это бесполезно для вышеуказанного требования, так как ASP. Net повторно использует экземпляр SomeController, который он создает, и не вызывает Dispose (удаление bool) в конце каждого выполнения API. Это наиболее разумно и совсем не удивительно.

HttpContext имеет крючок DisposeOnPipelineCompleted(callbackObject), который выглядел многообещающе. К сожалению, для наших целей это серьезный недостаток: он вызывается в потоке, отличном от того, в котором выполнялся API, и потоки API используются повторно. Поскольку вызов callbackObject.Dispose(), таким образом, асинхронен с потоком, который его зарегистрировал, обратный вызов может и часто делает, только начинает выполнение после потока, для которого мы пытаемся выполнить очистку между вызовами API. , начинает выполнение другого вызова API. Другими словами, если API SomePostApi выполняется в потоке A, и он регистрирует объект обратного вызова очистки CleanupThreadObject, то после того, как SomePostApi завершит выполнение (в потоке A), поток A вполне может начать выполнение * 1023. * для какого-то другого пользователя, прежде чем CleanupThreadObject.Dispose() будет вызван в потоке B, в котором он был запланирован для запуска, даже если регистрационный вызов был сделан из потока A.

Есть ли другие * хуки в * 1038? *. NET, что позволило бы нам регистрировать обратный вызов или выполнять общий код синхронно в конце каждого вызова API контроллера?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...