Obtenez l'adresse IP de l'hôte distant

136

Dans ASP.NET, il existe une System.Web.HttpRequestclasse, qui contient une ServerVariablespropriété qui peut nous fournir l'adresse IP à partir de la REMOTE_ADDRvaleur de la propriété.

Cependant, je n'ai pas pu trouver un moyen similaire d'obtenir l'adresse IP de l'hôte distant à partir de l'API Web ASP.NET.

Comment puis-je obtenir l'adresse IP de l'hôte distant qui fait la demande?

paulius_l
la source

Réponses:

189

Il est possible de le faire, mais pas très découvrable - vous devez utiliser le sac de propriétés de la demande entrante, et la propriété à laquelle vous devez accéder dépend si vous utilisez l'API Web sous IIS (hébergée sur le Web) ou auto-hébergée. Le code ci-dessous montre comment cela peut être fait.

private string GetClientIp(HttpRequestMessage request)
{
    if (request.Properties.ContainsKey("MS_HttpContext"))
    {
        return ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
    }

    if (request.Properties.ContainsKey(RemoteEndpointMessageProperty.Name))
    {
        RemoteEndpointMessageProperty prop;
        prop = (RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessageProperty.Name];
        return prop.Address;
    }

    return null;
}
carlosfigueira
la source
4
Merci, je cherchais ça aussi. Amélioration mineure = classe d'extension: gist.github.com/2653453
MikeJansen
28
Le WebAPI est pour la plupart très propre. C'est dommage qu'un code comme celui-ci soit nécessaire pour quelque chose de trivial en tant qu'IP.
Toad
2
La RemoteEndpointMessagePropertyclasse est-elle dans l' System.ServiceModel.Channelsespace de noms, l' System.ServiceModel.dllassembly? N'est-ce pas une assemblée appartenant à WCF?
Slauma
4
@Slauma, oui, ils le sont. L'API Web ASP.NET est (actuellement) implémentée en deux «versions», auto-hébergée et hébergée sur le Web. La version hébergée sur le Web est implémentée par-dessus ASP.NET, tandis que la version auto-hébergée par-dessus un écouteur WCF. Notez que la plate-forme (API Web ASP.NET) elle-même est indépendante de l'hébergement, il est donc possible que quelqu'un implémente un hébergement différent à l'avenir et que l'hôte présente cette propriété (point de terminaison distant) différemment.
carlosfigueira
3
Malheureusement, cela ne fonctionne pas si vous vous auto-hébergez avec Owin (comme cela est recommandé pour l'API Web 2). Besoin d'un autre s'il y a ...
Nikolai Samteladze
74

Cette solution couvre également les API Web auto-hébergées avec Owin. Partiellement d' ici .

Vous pouvez créer une méthode privée en vous ApiControllerqui retournera une adresse IP distante quelle que soit la façon dont vous hébergez votre API Web:

 private const string HttpContext = "MS_HttpContext";
 private const string RemoteEndpointMessage =
     "System.ServiceModel.Channels.RemoteEndpointMessageProperty";
 private const string OwinContext = "MS_OwinContext";

 private string GetClientIp(HttpRequestMessage request)
 {
       // Web-hosting
       if (request.Properties.ContainsKey(HttpContext ))
       {
            HttpContextWrapper ctx = 
                (HttpContextWrapper)request.Properties[HttpContext];
            if (ctx != null)
            {
                return ctx.Request.UserHostAddress;
            }
       }

       // Self-hosting
       if (request.Properties.ContainsKey(RemoteEndpointMessage))
       {
            RemoteEndpointMessageProperty remoteEndpoint =
                (RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessage];
            if (remoteEndpoint != null)
            {
                return remoteEndpoint.Address;
            }
        }

       // Self-hosting using Owin
       if (request.Properties.ContainsKey(OwinContext))
       {
           OwinContext owinContext = (OwinContext)request.Properties[OwinContext];
           if (owinContext != null)
           {
               return owinContext.Request.RemoteIpAddress;
           }
       }

        return null;
 }

Références requises:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (vous l'aurez déjà si vous utilisez le package Owin)

Un petit problème avec cette solution est que vous devez charger des bibliothèques pour les 3 cas où vous n'utiliserez en fait qu'une seule d'entre elles pendant l'exécution. Comme suggéré ici , cela peut être surmonté en utilisant des dynamicvariables. Vous pouvez également écrire une GetClientIpAddressméthode comme extension pour HttpRequestMethod.

using System.Net.Http;

public static class HttpRequestMessageExtensions
{
    private const string HttpContext = "MS_HttpContext";
    private const string RemoteEndpointMessage =
        "System.ServiceModel.Channels.RemoteEndpointMessageProperty";
    private const string OwinContext = "MS_OwinContext";

    public static string GetClientIpAddress(this HttpRequestMessage request)
    {
       // Web-hosting. Needs reference to System.Web.dll
       if (request.Properties.ContainsKey(HttpContext))
       {
           dynamic ctx = request.Properties[HttpContext];
           if (ctx != null)
           {
               return ctx.Request.UserHostAddress;
           }
       }

       // Self-hosting. Needs reference to System.ServiceModel.dll. 
       if (request.Properties.ContainsKey(RemoteEndpointMessage))
       {
            dynamic remoteEndpoint = request.Properties[RemoteEndpointMessage];
            if (remoteEndpoint != null)
            {
                return remoteEndpoint.Address;
            }
        }

       // Self-hosting using Owin. Needs reference to Microsoft.Owin.dll. 
       if (request.Properties.ContainsKey(OwinContext))
       {
           dynamic owinContext = request.Properties[OwinContext];
           if (owinContext != null)
           {
               return owinContext.Request.RemoteIpAddress;
           }
       }

        return null;
    }
}

Vous pouvez maintenant l'utiliser comme ceci:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}
Nikolai Samteladze
la source
1
Cette solution doit utiliser cet espace de noms «System.Net.Http» pour fonctionner. Puisqu'il s'agit d'un nom de classe sur Assembly System.Web.Http.dll, v5.2.2.0.
Wagner Bertolini Junior
@WagnerBertolini, vous avez raison, vous avez besoin d'une using System.Net.Http;ligne parce que vous étendez HttpRequestMessage. À moins que vous ne définissiez votre extension dans l' System.Net.Httpespace de noms, ce qui est très discutable. Pas sûr que ce soit essentiel, car il sera automatiquement ajouté par n'importe quel IDE ou outil de productivité. Qu'est-ce que tu penses?
Nikolai Samteladze
J'étais pressé d'essayer de terminer un travail ici et il m'a fallu plus de 20 minutes pour voir ce qui se passait sur l'erreur de construction. J'ai juste copié le code et créé une classe pour cela, lors de la compilation, il ne montrait pas les méthodes, quand j'ai utilisé "aller à la définition" sur VS, cela m'a emmené dans ma classe, et je n'arrête pas de comprendre ce qui se passe, jusqu'à ce que je a trouvé l'autre classe. Les extensions sont une fonctionnalité assez nouvelle et ne sont pas utilisées tout le temps, car je pense que ce serait une bonne idée de gagner du temps.
Wagner Bertolini Junior
1
Lorsque vous utilisez OWIN, vous pouvez simplement utiliser les extensions OwinHttpRequestMessageExtensions pour obtenir le contexte OWIN comme: request.GetOwinContext (). Request.RemoteIpAddress
Stef Heyenrath
1
Il devrait en fait être var ctx = request.Properties [MsHttpContext] comme HttpContextWrapper; Si vous lancez, vous n'avez pas besoin de vérifier la valeur nulle, car si la distribution échoue, vous obtenez une exception
Stef Heyenrath
31

Si vous voulez vraiment un one-liner et ne prévoyez pas d'auto-héberger l'API Web:

((System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"]).Request.UserHostAddress;
zacharydl
la source
13

Les réponses ci-dessus nécessitent une référence à System.Web pour pouvoir convertir la propriété en HttpContext ou HttpContextWrapper. Si vous ne voulez pas la référence, vous pouvez obtenir l'ip en utilisant une dynamique:

var host = ((dynamic)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
Robin van der Knaap
la source
-1

La solution fournie par carlosfigueira fonctionne, mais les one-liners de type sécurisé sont meilleurs: ajoutez un using System.Webaccès puis HttpContext.Current.Request.UserHostAddressdans votre méthode d'action.

Warren Rumak
la source
26
-1 cela ne peut pas être approuvé dans l'API Web car il HttpContext.Currentn'est pas conservé correctement tout au long du pipeline de tâches; puisque toute la gestion des demandes est asynchrone. HttpContext.Currentdoit presque toujours être évité lors de l'écriture de code API Web.
Andras Zoltan
@Andras, je voudrais savoir plus en détail pourquoi utiliser HttpContext.Current est mauvais, connaissez-vous une ressource précieuse pour cela?
cuongle
13
Salut @CuongLe; en fait - alors que le problème de threading peut être un problème (mais pas toujours directement si le SynchronizationContextflux est correctement effectué entre les tâches); le plus gros problème avec cela est si votre code de service est susceptible d'être auto-hébergé (test, par exemple) - HttpContext.Currentest une construction purement Asp.Net et n'existe pas lorsque vous vous auto-hébergez.
Andras Zoltan
La sécurité des types n'est pas tout. Ce code lancera un NullReferenceExceptionproblème difficile à déboguer s'il est utilisé à partir d'un thread (par exemple, un serveur de tâches, très courant dans le code API Web moderne) ou dans un contexte auto-hébergé. Au moins la plupart des autres réponses reviendront null.
Aaronaught