Uzyskaj adres IP zdalnego hosta

136

W ASP.NET istnieje System.Web.HttpRequestklasa, która zawiera ServerVariableswłaściwość, która może dostarczyć nam adres IP z REMOTE_ADDRwartości właściwości.

Jednak nie udało mi się znaleźć podobnego sposobu uzyskania adresu IP zdalnego hosta z ASP.NET Web API.

Jak mogę uzyskać adres IP zdalnego hosta, który wysyła żądanie?

paulius_l
źródło

Odpowiedzi:

189

Jest to możliwe, ale niezbyt wykrywalne - musisz użyć zestawu właściwości z przychodzącego żądania, a właściwość, do której chcesz uzyskać dostęp, zależy od tego, czy korzystasz z internetowego interfejsu API w usługach IIS (w sieci Web), czy na własnym serwerze. Poniższy kod pokazuje, jak można to zrobić.

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
źródło
4
Dzięki, ja też tego szukałem. Niewielka poprawa = klasa rozszerzenia: gist.github.com/2653453
MikeJansen
28
Interfejs WebAPI jest w większości bardzo przejrzysty. Szkoda, że ​​taki kod jest potrzebny do czegoś tak trywialnego jak adres IP.
Toad
2
Czy RemoteEndpointMessagePropertyklasa jest w System.ServiceModel.Channelsprzestrzeni nazw, System.ServiceModel.dllzestawie? Czy to nie jest zestaw należący do WCF?
Slauma
4
@Slauma, tak, są. Interfejs API sieci Web ASP.NET jest (obecnie) zaimplementowany w dwóch „odmianach”: samodzielnie hostowany i hostowany w sieci Web. Wersja hostowana w sieci Web jest zaimplementowana na platformie ASP.NET, podczas gdy wersja hostowana samodzielnie na odbiorniku WCF. Zwróć uwagę, że sama platforma (ASP.NET Web API) jest hostem niezależna od hostingu, więc możliwe jest, że ktoś w przyszłości zaimplementuje inny hosting, a host będzie inaczej wyświetlał tę właściwość (zdalny punkt końcowy).
carlosfigueira
3
Niestety, to nie działa, jeśli hostujesz samodzielnie przy użyciu Owin (co jest zalecane w przypadku interfejsu API sieci Web 2). Potrzebujesz innego, jeśli tam ...
Nikolai Samteladze
74

To rozwiązanie obejmuje również samoobsługowy interfejs API sieci Web przy użyciu Owin. Częściowo stąd .

Możesz utworzyć prywatną metodę, ApiControllerktóra będzie zwracać zdalny adres IP bez względu na sposób hostowania internetowego interfejsu API:

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

Wymagane referencje:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (będziesz go już mieć, jeśli używasz pakietu Owin)

Mały problem z tym rozwiązaniem polega na tym, że musisz załadować biblioteki dla wszystkich 3 przypadków, gdy faktycznie będziesz używać tylko jednej z nich podczas działania. Jak sugerowano tutaj , można temu zaradzić za pomocą dynamiczmiennych. Możesz również napisać GetClientIpAddressmetodę jako rozszerzenie dla 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;
    }
}

Teraz możesz go używać w ten sposób:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}
Nikolai Samteladze
źródło
1
To rozwiązanie powinno używać do działania przestrzeni nazw „System.Net.Http”. Ponieważ jest to nazwa klasy w Assembly System.Web.Http.dll, v5.2.2.0.
Wagner Bertolini Junior
@WagnerBertolini, masz rację, potrzebujesz using System.Net.Http;linii, ponieważ przedłużasz HttpRequestMessage. Chyba że definiujesz swoje rozszerzenie w System.Net.Httpprzestrzeni nazw, co jest bardzo wątpliwe. Nie jestem pewien, czy jest to niezbędne, ponieważ zostanie automatycznie dodane przez dowolne narzędzie IDE lub narzędzie zwiększające produktywność. Co myślisz?
Nikolai Samteladze
Spieszyłem się, próbując ukończyć jedną pracę tutaj i zajęło mi ponad 20 minut, aby zobaczyć, co się dzieje z błędem kompilacji. Po prostu skopiowałem kod i utworzyłem dla niego klasę, podczas kompilacji nie pokazywałem metod, kiedy użyłem "przejdź do definicji" na VS, zabrało mnie to do mojej klasy i wciąż nie rozumiem, co się dzieje, dopóki nie znalazł drugą klasę. Rozszerzenia to całkiem nowa funkcja i nie jest używana przez cały czas, ponieważ myślę, że warto byłoby zaoszczędzić ten czas.
Wagner Bertolini Junior
1
Korzystając z OWIN, możesz po prostu użyć OwinHttpRequestMessageExtensions, aby uzyskać kontekst OWIN, taki jak: request.GetOwinContext (). Request.RemoteIpAddress
Stef Heyenrath
1
W rzeczywistości powinno to być var ​​ctx = request.Properties [MsHttpContext] jako HttpContextWrapper;
EJeśli rzucasz
31

Jeśli naprawdę potrzebujesz jednowierszowego rozwiązania i nie planujesz samodzielnego hostowania internetowego interfejsu API:

((System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"]).Request.UserHostAddress;
zacharydl
źródło
13

Powyższe odpowiedzi wymagają odwołania do System.Web, aby móc rzutować właściwość na HttpContext lub HttpContextWrapper. Jeśli nie chcesz odniesienia, możesz uzyskać adres IP za pomocą dynamicznego:

var host = ((dynamic)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
Robin van der Knaap
źródło
-1

Rozwiązanie dostarczone przez carlosfigueira działa, ale jednowierszowe z bezpiecznym typem są lepsze: dodaj a using System.Webthen access HttpContext.Current.Request.UserHostAddressw swojej metodzie działania.

Warren Rumak
źródło
26
-1 nie można ufać temu interfejsowi API sieci Web, ponieważ HttpContext.Currentnie jest on poprawnie utrwalany w potoku zadań; ponieważ cała obsługa żądań jest asynchroniczna. HttpContext.Currentnależy prawie zawsze unikać podczas pisania kodu interfejsu API sieci Web.
Andras Zoltan
@ Andras, chciałbym dowiedzieć się więcej szczegółów, dlaczego używanie HttpContext.Current jest złe, czy znasz jakieś cenne źródło informacji na ten temat?
cuongle
13
Cześć @CuongLe; w rzeczywistości - podczas gdy problem z wątkami może być problemem (chociaż nie zawsze bezpośrednio, jeśli SynchronizationContextprzepływa poprawnie między zadaniami); Największym problemem jest to, że kod usługi może kiedykolwiek znajdować się na własnym serwerze (na przykład testowanie) - HttpContext.Currentjest konstrukcją czysto Asp.Net i nie istnieje podczas samodzielnego hostowania.
Andras Zoltan
Bezpieczeństwo typów to nie wszystko. Ten kod wyrzuci trudny do debugowania, NullReferenceExceptionjeśli zostanie użyty z wątku (np. Narzędzie oczekujące na zadanie, bardzo powszechne we współczesnym kodzie interfejsu API sieci Web) lub w kontekście hostowanym samodzielnie. Przynajmniej większość innych odpowiedzi po prostu wróci null.
Aaronaught