web-dev-qa-db-fra.com

L'utilisation de HttpContext.Current dans Web Api est dangereuse en raison de l'async

Ma question est un peu liée à ceci: équivalent WebApi pour HttpContext.Items with Dependency Injection .

Nous voulons injecter une classe en utilisant HttpContext.Current dans la zone WebApi en utilisant Ninject.

Mon souci est, cela pourrait être très dangereux , comme dans WebApi ( tout?) est asynchrone.

S'il vous plaît, corrigez-moi si je me trompe, voici ce que j'ai étudié jusqu'à présent:

  1. HttpContext.Current récupère le contexte actuel par Thread (j'ai examiné directement l'implémentation).

  2. L'utilisation de HttpContext.Current à l'intérieur de la tâche asynchrone n'est pas possible car elle peut s'exécuter sur un autre thread.

  3. WebApi utilise IHttpController avec la méthode Task<HttpResponseMessage> ExecuteAsync => chaque requête est asynchrone => vous ne pouvez pas utiliser HttpContext.Current dans la méthode action. Cela pourrait même arriver, plus de demandes sont exécutées sur le même fil par coicidence.

  4. Pour créer des contrôleurs avec des éléments injectés dans les constructeurs, IHttpControllerActivator est utilisé avec la méthode sync IHttpController Create. C'est là que ninject crée Controller avec toutes ses dépendances.


  • Si je suis correct dans l'ensemble de ces 4 points, l'utilisation de HttpContext.Current à l'intérieur d'une méthode d'action ou de l'un des calques ci-dessous est très dangereuse et peut avoir des résultats inattendus. J'ai vu sur SO beaucoup de réponses acceptées suggérant exactement ceci. IMHO cela peut fonctionner pendant un certain temps, mais échouera sous la charge.

  • Mais lorsque vous utilisez DI pour créer un contrôleur et ses dépendances, il est correct car il s’exécute sur un thread séparé. Je pourrais obtenir une valeur du HttpContext dans le constructeur et ce serait sûr? . Je me demande si chaque contrôleur est créé sur un seul thread pour chaque requête, car cela pourrait poser un problème sous de lourdes charges, où tous les threads de IIS pourraient être consommés.

Juste pour expliquer pourquoi je veux injecter des choses dans HttpContext:

  • une solution serait d’obtenir la requête dans la méthode d’action du contrôleur et de transmettre la valeur requise à toutes les couches en tant que paramètres jusqu’à ce qu’elle soit utilisée quelque part au fond du code.
  • notre solution souhaitée: toutes les couches intermédiaires ne sont pas affectées par cela, et nous pouvons utiliser la requête injectée quelque part au fond du code (par exemple, dans certains ConfigurationProvider qui dépend de l'URL)

    S'il vous plaît donnez-moi votre opinion si je me trompe totalement ou si mes suggestions sont correctes, car ce thème semble être très compliqué. Merci d'avance!

58
Lukas K

J'utilise une API Web qui utilise la méthodologie async/wait.

en utilisant également

1) HttpContext.Current.Server.MapPath
2) System.Web.HttpContext.Current.Request.ServerVariables

Cela fonctionnait bien pendant un bon bout de temps, ce qui a soudainement éclaté sans qu'aucun code ne soit modifié.

En passant beaucoup de temps en revenant aux anciennes versions précédentes, la clé manquante était à l'origine du problème.

< httpRuntime targetFramework="4.5.2"  /> under system.web

Je ne suis pas un expert techniquement. Mais je suggère d’ajouter la clé à votre configuration Web et de lui donner un GO .

4
Selva Ramaswamy

J'ai trouvé un très bon article décrivant exactement ce problème: http://byterot.blogspot.cz/2012/04/aspnet-web-api-series-part-3-async-deep.html?m=1

l’auteur a étudié en profondeur comment la méthode ExecuteAsync s’appelle dans le cadre WebApi et est parvenue à cette conclusion:

Les actions de l'API Web ASP.NET (et toutes les méthodes de pipeline) ne seront appelées de manière asynchrone que si vous renvoyez une tâche ou une tâche <T>. Cela peut sembler évident, mais aucune des méthodes de pipeline avec le suffixe Async ne s'exécutera dans leurs propres threads. L'utilisation de la couverture Async pourrait être un abus de langage. [UPDATE: l'équipe ASP.NET a en effet confirmé que Async est utilisé pour désigner les méthodes qui renvoient une tâche et peuvent s'exécuter de manière asynchrone mais ne doivent pas obligatoirement]

D'après ce que j'ai compris de l'article, les méthodes Action sont appelées de manière synchrone, mais c'est la décision de l'appelant.

J'ai créé une petite application de test à cet effet, quelque chose comme ceci:

public class ValuesController : ApiController
{
    public object Get(string clientId, string specialValue)
    {
        HttpRequest staticContext = HttpContext.Current.Request;
        string staticUrl = staticContext.Url.ToString();

        HttpRequestMessage dynamicContext = Request;
        string dynamicUrl = dynamicContext.RequestUri.ToString();

        return new {one = staticUrl, two = dynamicUrl};
    }
}

et une version asynchrone retournant async Task<object>

J'ai essayé de faire une petite attaque DOS avec jQuery et je ne pouvais déterminer aucun problème avant d'utiliser await Task.Delay(1).ConfigureAwait(false);, ce qui est évident.

Ce que j'ai compris de l'article, c'est que le problème est très compliqué et qu'un commutateur de thread peut se produire lors de l'utilisation d'une méthode d'action asynchrone, il est donc défini [~ # ~] et non [~ # ~] une bonne idée d'utiliser HttpContext.Current n'importe où dans le code appelé à partir des méthodes d'action. Mais comme le contrôleur est créé de manière synchrone, l’utilisation de HttpContext.Current dans le constructeur ainsi que dans l’injection de dépendance est correcte.

Si quelqu'un a une autre explication à ce problème, corrigez-moi car ce problème est très compliqué et je ne suis toujours pas convaincu à 100%.

diclaimer:

  • J'ignore pour l'instant le problème de l'API Web auto-hébergée avec IIS, où HttpContext.Current ne fonctionnerait probablement pas de toute façon. Nous comptons maintenant sur IIS.
2
Lukas K