Получить IP-адрес удаленного хоста

136

В ASP.NET есть System.Web.HttpRequestкласс, который содержит ServerVariablesсвойство, которое может предоставить нам IP-адрес из REMOTE_ADDRзначения свойства.

Однако я не смог найти аналогичный способ получения IP-адреса удаленного хоста из ASP.NET Web API.

Как я могу получить IP-адрес удаленного хоста, который делает запрос?

paulius_l
источник

Ответы:

189

Это можно сделать, но не очень легко обнаружить - вам нужно использовать пакет свойств из входящего запроса, а свойство, к которому вам нужно получить доступ, зависит от того, используете ли вы веб-API в IIS (с веб-хостингом) или самостоятельно размещенный. Код ниже показывает, как это можно сделать.

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
источник
4
Спасибо, я тоже искал это. Незначительное улучшение = класс расширения: gist.github.com/2653453
MikeJansen
28
WebAPI по большей части очень чистый. Обидно, что такой код нужен для чего-то тривиального, например, для IP.
жаба
2
Это RemoteEndpointMessagePropertyкласс в System.ServiceModel.Channelsпространстве имен, System.ServiceModel.dllсборка? Разве это не собрание, принадлежащее WCF?
Слаума
4
@ Слаума, да, они есть. Веб-API ASP.NET (в настоящее время) реализован в двух «разновидностях»: самостоятельном и веб-размещении. Веб-версия реализована поверх ASP.NET, а собственная - поверх прослушивателя WCF. Обратите внимание, что сама платформа (ASP.NET Web API) не зависит от хостинга, поэтому возможно, что кто-то в будущем будет реализовывать другой хостинг, и хост будет по-разному отображать это свойство (удаленную конечную точку).
Carlosfigueira
3
К сожалению, это не работает, если вы самостоятельно размещаете хост с помощью Owin (как это рекомендуется для Web API 2). Нужен еще один, если есть ...
Николай Самтеладзе
74

Это решение также охватывает веб-API, размещенный самостоятельно с помощью Owin. Частично отсюда .

Вы можете создать частный метод, ApiControllerкоторый будет возвращать удаленный IP-адрес независимо от того, как вы размещаете свой веб-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;
 }

Требуются ссылки:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (он будет уже у вас, если вы используете пакет Owin)

Небольшая проблема с этим решением состоит в том, что вам нужно загружать библиотеки для всех 3 случаев, когда вы фактически будете использовать только один из них во время выполнения. Как предлагается здесь , это может быть преодолено с помощью dynamicпеременных. Вы также можете написать GetClientIpAddressметод как расширение для 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;
    }
}

Теперь вы можете использовать это так:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}
Николай Самтеладзе
источник
1
Это решение должно использовать это пространство имен "System.Net.Http" для работы. Так как это имя класса в сборке System.Web.Http.dll, v5.2.2.0.
Вагнер Бертолини Младший
@WagnerBertolini, вы правы, вам нужна using System.Net.Http;строка, потому что вы расширяете HttpRequestMessage. Если только вы не определяете свое расширение в System.Net.Httpпространстве имен, что очень сомнительно. Не уверен, что это важно, потому что он будет автоматически добавлен любым IDE или инструментом повышения производительности. Что вы думаете?
Николай Самтеладзе
Я спешил, пытаясь закончить одну работу здесь, и мне потребовалось более 20 минут, чтобы увидеть, что происходит при сборке. Я просто скопировал код и создал для него класс, когда при компиляции он не показывал методы, когда я использовал «перейти к определению» в VS, он привел меня в мой класс, и я не понимаю, что происходит, пока я нашел другой класс. Расширения - это довольно новая функция, которая используется не всегда, так как, я думаю, было бы неплохо сэкономить это время.
Вагнер Бертолини Младший
1
При использовании OWIN вы можете просто использовать OwinHttpRequestMessageExtensions для получения контекста OWIN, например: request.GetOwinContext (). Request.RemoteIpAddress
Stef Heyenrath
1
На самом деле это должно быть var ctx = request.Properties [MsHttpContext] as HttpContextWrapper; Если вы разыгрываете, вам не нужно проверять на ноль, потому что, если
разыгрывается
31

Если вы действительно хотите использовать одну строку и не планируете самостоятельно размещать веб-API:

((System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"]).Request.UserHostAddress;
zacharydl
источник
13

Приведенные выше ответы требуют ссылки на System.Web для приведения свойства к HttpContext или HttpContextWrapper. Если вы не хотите ссылку, вы можете получить IP с помощью динамического:

var host = ((dynamic)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
Робин ван дер Кнаап
источник
-1

Решение, предоставленное carlosfigueira, работает, но безопасные однострочники лучше: добавьте using System.Webзатем доступ HttpContext.Current.Request.UserHostAddressв свой метод действия.

Уоррен Румак
источник
26
-1 этому веб-интерфейсу нельзя доверять, поскольку HttpContext.Currentон не сохраняется корректно в конвейере задач; так как вся обработка запросов асинхронная. HttpContext.Currentпочти всегда следует избегать при написании кода веб-API.
Андрас Золтан
@ Андрас, я хотел бы узнать более подробно, почему использование HttpContext.Current плохо, знаете ли вы какой-либо ценный ресурс для этого?
cuongle
13
Привет @CuongLe; на самом деле - хотя проблема с многопоточностью может быть проблемой (хотя не всегда напрямую, если SynchronizationContextпоток правильно передается между задачами); самая большая проблема в этом случае, если ваш сервисный код, вероятно, когда-либо будет размещаться самостоятельно (например, для тестирования) - HttpContext.Currentэто чисто Asp.Net-конструкция, которая не существует, когда вы размещаетесь самостоятельно.
Андрас Золтан
Безопасность типов - это еще не все. Этот код NullReferenceExceptionсоздает сложность для отладки, если используется из потока (например, ожидающий задачи, очень распространенный в современном коде Web API) или в автономном контексте. По крайней мере, большинство других ответов просто вернутся null.
Aaronaught