Rufen Sie die IP-Adresse des Remote-Hosts ab

136

In ASP.NET gibt es eine System.Web.HttpRequestKlasse, die eine ServerVariablesEigenschaft enthält, die uns die IP-Adresse aus dem REMOTE_ADDREigenschaftswert bereitstellen kann .

Ich konnte jedoch keinen ähnlichen Weg finden, um die IP-Adresse des Remote-Hosts von der ASP.NET-Web-API abzurufen.

Wie kann ich die IP-Adresse des Remote-Hosts abrufen, der die Anforderung stellt?

paulius_l
quelle

Antworten:

189

Dies ist möglich, aber nicht sehr auffindbar. Sie müssen die Eigenschaftentasche aus der eingehenden Anforderung verwenden, und die Eigenschaft, auf die Sie zugreifen müssen, hängt davon ab, ob Sie die Web-API unter IIS (webhosted) oder selbst gehostet verwenden. Der folgende Code zeigt, wie dies getan werden kann.

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
quelle
4
Danke, ich habe auch danach gesucht. Kleinere Verbesserung = Erweiterungsklasse: gist.github.com/2653453
MikeJansen
28
Die WebAPI ist größtenteils sehr sauber. Es ist eine Schande, dass Code wie dieser für etwas Triviales als IP benötigt wird.
Kröte
2
Ist die RemoteEndpointMessagePropertyKlasse im System.ServiceModel.ChannelsNamespace, System.ServiceModel.dllAssembly? Ist das nicht eine Versammlung von WCF?
Slauma
4
@ Slauma, ja, das sind sie. Die ASP.NET-Web-API ist (derzeit) in zwei "Varianten" implementiert: selbst gehostet und web gehostet. Die im Web gehostete Version wird auf ASP.NET implementiert, während die selbst gehostete auf einem WCF-Listener implementiert wird. Beachten Sie, dass die Plattform (ASP.NET Web API) selbst hostunabhängig ist. Daher ist es möglich, dass in Zukunft jemand ein anderes Hosting implementiert und der Host diese Eigenschaft (Remote-Endpunkt) anders anzeigt.
Carlosfigueira
3
Leider funktioniert dies nicht, wenn Sie sich mit Owin selbst hosten (wie für Web API 2 empfohlen). Brauchen Sie noch einen, wenn da ...
Nikolai Samteladze
74

Diese Lösung deckt auch die mit Owin selbst gehostete Web-API ab. Teilweise von hier .

Sie können in sich eine private Methode erstellen ApiController, die die Remote-IP-Adresse zurückgibt, unabhängig davon, wie Sie Ihre Web-API hosten:

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

Referenzen erforderlich:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (Sie haben es bereits, wenn Sie das Owin-Paket verwenden)

Ein kleines Problem bei dieser Lösung ist, dass Sie Bibliotheken für alle drei Fälle laden müssen, wenn Sie zur Laufzeit tatsächlich nur eine davon verwenden. Wie hier vorgeschlagen , kann dies durch Verwendung von dynamicVariablen überwunden werden . Sie können die GetClientIpAddressMethode auch als Erweiterung für schreiben 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;
    }
}

Jetzt können Sie es so verwenden:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}
Nikolai Samteladze
quelle
1
Diese Lösung sollte diesen Namespace "System.Net.Http" verwenden, um zu funktionieren. Da dies ein Klassenname in Assembly System.Web.Http.dll, v5.2.2.0 ist.
Wagner Bertolini Junior
@WagnerBertolini, Sie sind richtig, Sie brauchen using System.Net.Http;Linie, weil Sie verlängern HttpRequestMessage. Es sei denn, Sie definieren Ihre Erweiterung im System.Net.HttpNamespace, was sehr fragwürdig ist. Ich bin mir jedoch nicht sicher, ob dies unbedingt erforderlich ist, da es von jeder IDE oder jedem Produktivitätswerkzeug automatisch hinzugefügt wird. Was denken Sie?
Nikolai Samteladze
Ich hatte es eilig, einen Job hier zu beenden, und es dauerte mehr als 20 Minuten, um zu sehen, was mit dem Build-Fehler los ist. Ich habe nur den Code kopiert und eine Klasse dafür erstellt. Beim Kompilieren wurden die Methoden nicht angezeigt. Als ich bei VS "Gehe zur Definition" verwendet habe, bin ich zu meiner Klasse gekommen, und ich verstehe immer wieder nicht, was los ist, bis ich fand die andere Klasse. Erweiterungen sind eine ziemlich neue Funktion und werden nicht immer verwendet, da ich denke, dass es eine gute Idee wäre, diese Zeit zu sparen.
Wagner Bertolini Junior
1
Wenn Sie OWIN verwenden, können Sie einfach die OwinHttpRequestMessageExtensions verwenden, um den OWIN-Kontext wie folgt abzurufen: request.GetOwinContext (). Request.RemoteIpAddress
Stef Heyenrath
1
Es sollte eigentlich var ctx = request.Properties [MsHttpContext] als HttpContextWrapper sein; EWenn Sie besetzen, müssen Sie nicht auf null prüfen, denn wenn die Besetzung fehlschlägt, erhalten Sie eine Ausnahme
Stef Heyenrath
31

Wenn Sie wirklich einen Einzeiler möchten und nicht vorhaben, die Web-API selbst zu hosten:

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

Die obigen Antworten erfordern einen Verweis auf System.Web, um die Eigenschaft in HttpContext oder HttpContextWrapper umwandeln zu können. Wenn Sie die Referenz nicht möchten, können Sie die IP mithilfe einer Dynamik abrufen:

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

Die von carlosfigueira bereitgestellte Lösung funktioniert, aber typsichere Einzeiler sind besser: Fügen Sie Ihrer Aktionsmethode einen using System.WebZugriff hinzu HttpContext.Current.Request.UserHostAddress.

Warren Rumak
quelle
26
-1 Dies kann in der Web-API nicht als vertrauenswürdig eingestuft werden, da HttpContext.Currentes in der gesamten Task-Pipeline nicht korrekt beibehalten wird. da die gesamte Anforderungsbehandlung asynchron ist. HttpContext.Currentsollte beim Schreiben von Web-API-Code fast immer vermieden werden.
Andras Zoltan
@Andras, ich würde gerne mehr Details wissen, warum die Verwendung von HttpContext.Current schlecht ist. Kennen Sie eine wertvolle Ressource dafür?
Cuongle
13
Hi @CuongLe; in der Tat - während das Threading-Problem ein Problem sein kann (obwohl nicht immer direkt, wenn das SynchronizationContextzwischen den Aufgaben korrekt fließt); Das größte Problem dabei ist, ob Ihr Service-Code wahrscheinlich jemals selbst gehostet wird (z. B. Testen) - HttpContext.Currentist ein reines Asp.Net-Konstrukt und existiert nicht, wenn Sie sich selbst hosten.
Andras Zoltan
Typensicherheit ist nicht alles. Dieser Code NullReferenceExceptionlöst ein schwer zu debuggendes Element aus, wenn er von einem Thread (z. B. einem Task-Wartenden, der im modernen Web-API-Code sehr häufig verwendet wird) oder in einem selbst gehosteten Kontext verwendet wird. Zumindest die meisten anderen Antworten werden einfach zurückkehren null.
Aaronaught