Obtenga la dirección IP del host remoto

136

En ASP.NET hay una System.Web.HttpRequestclase, que contiene una ServerVariablespropiedad que nos puede proporcionar la dirección IP del REMOTE_ADDRvalor de la propiedad.

Sin embargo, no pude encontrar una forma similar de obtener la dirección IP del host remoto de la API web de ASP.NET.

¿Cómo puedo obtener la dirección IP del host remoto que realiza la solicitud?

paulius_l
fuente

Respuestas:

189

Es posible hacer eso, pero no es muy reconocible: debe usar la bolsa de propiedades de la solicitud entrante, y la propiedad a la que necesita acceder depende de si está utilizando la API web en IIS (alojamiento web) o autohospedado. El siguiente código muestra cómo se puede hacer esto.

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
fuente
44
Gracias, estaba buscando esto también. Mejora menor = clase de extensión: gist.github.com/2653453
MikeJansen
28
El WebAPI es en su mayor parte muy limpio. Es una pena que se necesite un código como este para algo trivial como una IP.
Sapo
2
¿Es la RemoteEndpointMessagePropertyclase en el System.ServiceModel.Channelsespacio de nombres, System.ServiceModel.dllensamblaje? ¿No es una asamblea perteneciente a WCF?
Slauma
44
@Slauma, sí, lo son. ASP.NET Web API se implementa (actualmente) en dos "sabores", autohospedado y hospedado en la web. La versión alojada en la web se implementa sobre ASP.NET, mientras que la versión autohospedada sobre un oyente WCF. Tenga en cuenta que la plataforma (ASP.NET Web API) en sí es independiente del alojamiento, por lo que es posible que alguien implemente un alojamiento diferente en el futuro y el host muestre esa propiedad (punto final remoto) de manera diferente.
carlosfigueira
3
Desafortunadamente, esto no funciona si se autohospeda usando Owin (como se recomienda para Web API 2). Necesito otro si hay ...
Nikolai Samteladze
74

Esta solución también cubre la API web autohospedada usando Owin. Parcialmente desde aquí .

Puede crear un método privado en usted ApiControllerque devolverá la dirección IP remota sin importar cómo aloje su 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;
 }

Referencias requeridas:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (ya lo tendrá si usa el paquete Owin)

Un pequeño problema con esta solución es que debe cargar bibliotecas para los 3 casos en los que realmente usará solo uno de ellos durante el tiempo de ejecución. Como se sugiere aquí , esto se puede superar mediante el uso de dynamicvariables. También puede escribir el GetClientIpAddressmétodo como una extensión para 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;
    }
}

Ahora puedes usarlo así:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}
Nikolai Samteladze
fuente
1
Esta solución debería usar este espacio de nombres "System.Net.Http" para funcionar. Dado que este es un nombre de clase en Assembly System.Web.Http.dll, v5.2.2.0.
Wagner Bertolini Junior
@WagnerBertolini, estás en lo correcto, necesitas using System.Net.Http;línea porque estás extendiendo HttpRequestMessage. A menos que esté definiendo su extensión en el System.Net.Httpespacio de nombres que es muy cuestionable. Sin embargo, no estoy seguro de que sea esencial, ya que cualquier herramienta IDE o de productividad la agregará automáticamente. ¿Qué piensas?
Nikolai Samteladze
Tenía prisa tratando de terminar un trabajo aquí y me llevó más de 20 minutos ver qué pasaba con el error de compilación. Simplemente copié el código y creé una clase para él, cuando la compilación no mostraba los métodos, cuando usé "ir a definición" en VS me llevó a mi clase, y sigo sin entender lo que está pasando, hasta que Encontré la otra clase. Las extensiones son una característica bastante nueva y no se usan todo el tiempo, ya que creo que sería una buena idea ahorrar esta vez.
Wagner Bertolini Junior
1
Cuando use OWIN, puede usar OwinHttpRequestMessageExtensions para obtener el contexto OWIN como: request.GetOwinContext (). Request.RemoteIpAddress
Stef Heyenrath
1
En realidad, debería ser var ctx = request.Properties [MsHttpContext] como HttpContextWrapper; ESi lanzas, no necesitas verificar nulo porque si el lanzamiento falla, obtienes una excepción
Stef Heyenrath
31

Si realmente desea una línea única y no planea auto-hospedar API web:

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

Las respuestas anteriores requieren una referencia a System.Web para poder convertir la propiedad a HttpContext o HttpContextWrapper. Si no desea la referencia, puede obtener la ip utilizando una dinámica:

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

La solución proporcionada por carlosfigueira funciona, pero las frases sencillas de tipo seguro son mejores: agregue un using System.Webacceso HttpContext.Current.Request.UserHostAddressen su método de acción.

Warren Rumak
fuente
26
-1 no se puede confiar en esto en la API web porque HttpContext.Currentno se persiste correctamente en todo el proceso de tareas; ya que todo el manejo de solicitudes es asíncrono. HttpContext.Currentcasi siempre debe evitarse al escribir código de API web.
Andras Zoltan el
@Andras, me gustaría saber más detalles por qué usar HttpContext.Current es malo, ¿conoces algún recurso valioso para esto?
cuongle
13
Hola @CuongLe de hecho, mientras que el tema de subprocesos puede ser un problema (aunque no siempre directamente si SynchronizationContextfluye correctamente entre tareas); El mayor problema con esto es si es probable que su código de servicio sea HttpContext.Currentautohospedado (prueba, por ejemplo): es una construcción puramente Asp.Net y no existe cuando se autohospeda.
Andras Zoltan
La seguridad tipográfica no lo es todo. Este código arrojará una NullReferenceExceptionsolución difícil de depurar si se usa desde un hilo (por ejemplo, un administrador de tareas, muy común en el código moderno de API web) o en un contexto autohospedado. Al menos la mayoría de las otras respuestas simplemente regresarán null.
Aaronaught