Ottieni l'indirizzo IP dell'host remoto


136

In ASP.NET esiste una System.Web.HttpRequestclasse che contiene ServerVariablesproprietà che possono fornirci l'indirizzo IP dal REMOTE_ADDRvalore della proprietà.

Tuttavia, non sono riuscito a trovare un modo simile per ottenere l'indirizzo IP dell'host remoto dall'API Web ASP.NET.

Come posso ottenere l'indirizzo IP dell'host remoto che sta effettuando la richiesta?

Risposte:


189

È possibile farlo, ma non molto rilevabile: è necessario utilizzare il bag della proprietà dalla richiesta in arrivo e la proprietà a cui è necessario accedere dipende dal fatto che si stia utilizzando l'API Web in IIS (webhosted) o in hosting autonomo. Il codice seguente mostra come eseguire questa operazione.

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;
}

4
Grazie, stavo cercando anche questo. Miglioramento minore = classe di estensione: gist.github.com/2653453
MikeJansen,

28
La WebAPI è per la maggior parte molto pulita. È un peccato che codice come questo sia necessario per qualcosa di banale come un IP.
Rospo,

2
La RemoteEndpointMessagePropertyclasse è nello System.ServiceModel.Channelsspazio dei nomi, System.ServiceModel.dllassembly? Non è un assembly appartenente a WCF?
Slauma,

4
@Slauma, sì, lo sono. L'API Web ASP.NET è (attualmente) implementata in due "versioni", self-hosted e web-hosted. La versione ospitata sul web è implementata su ASP.NET, mentre quella ospitata su un listener WCF. Si noti che la piattaforma (ASP.NET Web API) stessa è indipendente dall'hosting, quindi è possibile che qualcuno implementerà un hosting diverso in futuro e che l'host presenterà tale proprietà (endpoint remoto) in modo diverso.
carlosfigueira,

3
Sfortunatamente, questo non funziona se fai l'hosting automatico utilizzando Owin (come è raccomandato per l'API Web 2).
Ho

74

Questa soluzione copre anche l'API Web ospitata autonomamente tramite Owin. Parzialmente da qui .

Puoi creare un metodo privato in te ApiControllerche restituirà l'indirizzo IP remoto, indipendentemente da come si ospita l'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;
 }

Riferimenti richiesti:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (lo avrai già se usi il pacchetto Owin)

Un piccolo problema con questa soluzione è che devi caricare le librerie per tutti e 3 i casi quando in realtà ne utilizzerai solo uno durante il runtime. Come suggerito qui , questo può essere superato usando le dynamicvariabili. Puoi anche scrivere il GetClientIpAddressmetodo come estensione per 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;
    }
}

Ora puoi usarlo in questo modo:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}

1
Questa soluzione dovrebbe utilizzare questo spazio dei nomi "System.Net.Http" per funzionare. Poiché questo è un nome di classe su Assembly System.Web.Http.dll, v5.2.2.0.
Wagner Bertolini Junior,

@WagnerBertolini, hai ragione, hai bisogno di una using System.Net.Http;linea perché stai estendendo HttpRequestMessage. A meno che tu non stia definendo la tua estensione nello System.Net.Httpspazio dei nomi, il che è molto discutibile. Non sono sicuro che sia essenziale, perché verrà automaticamente aggiunto da qualsiasi IDE o strumento di produttività. Cosa ne pensi?
Nikolai Samteladze,

Ero di fretta cercando di finire un lavoro qui e mi ci sono voluti più di 20 minuti per vedere cosa stava succedendo errore di compilazione. Ho appena copiato il codice e creato una classe per esso, durante la compilazione non mostrava i metodi, quando ho usato "vai alla definizione" su VS mi ha portato alla mia classe e continuo a non capire cosa succede, fino a quando ho trovato l'altra classe. Le estensioni sono una funzionalità piuttosto nuova e non vengono sempre utilizzate, poiché penso che sarebbe una buona idea risparmiare tempo.
Wagner Bertolini Junior,

1
Quando usi OWIN, puoi semplicemente usare OwinHttpRequestMessageExtensions per ottenere il contesto OWIN come: request.GetOwinContext (). Request.RemoteIpAddress
Stef Heyenrath

1
In realtà dovrebbe essere var ctx = request.Properties [MsHttpContext] come HttpContextWrapper; Se esegui il cast, non è necessario controllare null perché se il cast fallisce, ottieni un'eccezione
Stef Heyenrath,

31

Se vuoi davvero un one-liner e non prevedi di ospitare l'API Web self-host:

((System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"]).Request.UserHostAddress;

13

Le risposte sopra richiedono un riferimento a System.Web per poter trasmettere la proprietà a HttpContext o HttpContextWrapper. Se non si desidera il riferimento, è possibile ottenere l'ip utilizzando una dinamica:

var host = ((dynamic)request.Properties["MS_HttpContext"]).Request.UserHostAddress;

-1

La soluzione fornita da carlosfigueira funziona, ma le battute con una sola riga di sicurezza sono migliori: aggiungi un using System.Webaccesso quindi HttpContext.Current.Request.UserHostAddressnel tuo metodo di azione.


26
-1 questo non può essere considerato attendibile nell'API Web perché HttpContext.Currentnon è persistito correttamente in tutta la pipeline delle attività; poiché tutta la gestione delle richieste è asincrona. HttpContext.Currentdovrebbe quasi sempre essere evitato quando si scrive il codice API Web.
Andras Zoltan,

@Andras, vorrei sapere più in dettaglio perché usare HttpContext. La corrente è male, conosci qualche risorsa preziosa per questo?
cuongle

13
Ciao @CuongLe; infatti - mentre il problema del threading può essere un problema (anche se non sempre direttamente se SynchronizationContextscorre correttamente tra le attività); il problema più grande con questo è se è probabile che il tuo codice di servizio sia auto-ospitato (test, per esempio) - HttpContext.Currentè un costrutto puramente Asp.Net e non esiste quando fai l'hosting.
Andras Zoltan,

La sicurezza del tipo non è tutto. Questo codice genererà un debug difficile NullReferenceExceptionda utilizzare se utilizzato da un thread (ad esempio un cameriere di attività, molto comune nel moderno codice API Web) o in un contesto ospitato da solo. Almeno la maggior parte delle altre risposte tornerà null.
Aaronaught
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.