获取远程主机的IP地址


136

在ASP.NET中,有一个System.Web.HttpRequest类,其中包含ServerVariables可以从REMOTE_ADDR属性值提供IP地址的属性。

但是,我找不到从ASP.NET Web API获取远程主机IP地址的类似方法。

如何获得发出请求的远程主机的IP地址?

Answers:


189

可以这样做,但不是很容易发现-您需要使用传入请求中的属性包,而需要访问的属性取决于您是在IIS(网络托管)还是自托管下使用Web API。下面的代码显示了如何完成此操作。

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
谢谢,我也在寻找这个。轻微改进=扩展程序类:gist.github.com/2653453
MikeJansen,2012年

28
WebAPI在大多数情况下非常干净。对于像IP这样琐碎的事情需要这样的代码真是可惜。
蟾蜍

2
RemoteEndpointMessageProperty在类System.ServiceModel.Channels的命名空间,System.ServiceModel.dll组装?那不是属于WCF的程序集吗?
Slauma 2012年

4
@Slauma,是的,他们是。ASP.NET Web API(当前)以两种“形式”实现,即自托管和Web托管。Web托管版本在ASP.NET之上实现,而自托管版本在WCF侦听器之上。请注意,该平台(ASP.NET Web API)本身与托​​管无关,因此将来有人可能实现不同的托管,并且该主机将以不同的方式显示该属性(远程端点)。
carlosfigueira,2012年

3
不幸的是,如果您使用Owin进行自我托管,这是行不通的(因为Web API 2建议这样做)。如果需要的话,还需要另一个...
Nikolai Samteladze 2013年

74

该解决方案还涵盖了使用Owin自托管的Web API。部分地从这里

您可以在其中创建一个私有方法,ApiController无论您如何托管Web API,该方法都将返回远程IP地址:

 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变量。您也可以将GetClientIpAddressmethod 编写为的扩展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”来工作。由于这是Assembly System.Web.Http.dll v5.2.2.0上的类名。
Wagner Bertolini Junior

@WagnerBertolini,您的意思是正确的,using System.Net.Http;因为您正在扩展,所以需要一行HttpRequestMessage。除非您要在System.Net.Http命名空间中定义扩展名,否则这是非常有问题的。但是不确定它是否必不可少,因为它将由任何IDE或生产力工具自动添加。你怎么看?
Nikolai Samteladze 2015年

我很着急地试图在这里完成一份工作,然后花了我超过20分钟的时间才能看到构建错误的原因。我只是复制了代码并为其创建了一个类,在编译时未显示方法,当我在VS上使用“转到定义”时,我进入了我的类,直到我不明白发生了什么,找到另一个班级。扩展是一个非常新的功能,并非一直使用,因为我认为节省时间是个好主意。
Wagner Bertolini Junior

1
使用OWIN时,您可以仅使用OwinHttpRequestMessageExtensions来获取OWIN上下文,例如:request.GetOwinContext()。Request.RemoteIpAddress
Stef Heyenrath '16

1
实际上应该是var ctx = request.Properties [MsHttpContext] as HttpContextWrapper; E如果您进行
强制转换

31

如果您真的想要单行并且不打算自托管Web API,请执行以下操作:

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

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,这在Web api中不可信任,因为HttpContext.Current在整个任务管道中无法正确持久保存;因为所有请求处理都是异步的。HttpContext.Current编写Web API代码时应几乎始终避免使用。
安德拉斯·佐坦

@Andras,我想知道更多使用HttpContext.Current为什么不好的细节,您知道任何有价值的资源吗?
cuongle

13
嗨@CuongLe; 实际上-尽管线程问题可能是个问题(但如果SynchronizationContext在任务之间正确流动,则可能并非总是如此);最大的问题是您的服务代码是否有可能是自托管的(例如,测试)- HttpContext.Current是纯Asp.Net结构,自托管时不存在。
安德拉斯·佐坦

类型安全不是全部。NullReferenceException如果从线程(例如,任务等待者,在现代Web API代码中非常常见)或在自托管上下文中使用,则此代码将引发难以调试的事件。至少大多数其他答案只会返回null
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.