获取远程主机的IP地址

Posted

技术标签:

【中文标题】获取远程主机的IP地址【英文标题】:Get the IP address of the remote host 【发布时间】:2012-03-22 21:07:22 【问题描述】:

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

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

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

【问题讨论】:

我找到的最简单的方法在这里:***.com/questions/53211824/… 【参考方案1】:

可以这样做,但不是很容易发现 - 您需要使用传入请求中的属性包,而您需要访问的属性取决于您是使用 IIS(webhosted)下的 Web API 还是 self -托管。下面的代码展示了如何做到这一点。

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;

【讨论】:

谢谢,我也在找这个。小改进 = 扩展类:gist.github.com/2653453 WebAPI 大部分都非常干净。很遗憾,像 IP 这样的微不足道的东西需要这样的代码。 RemoteEndpointMessagePropertySystem.ServiceModel.Channels 命名空间System.ServiceModel.dll 程序集中的类吗?那不是属于 WCF 的程序集吗? @Slauma,是的,他们是。 ASP.NET Web API(当前)以两种“风格”实现,自托管和网络托管。 Web 托管版本是在 ASP.NET 之上实现的,而自托管版本是在 WCF 侦听器之上实现的。请注意,平台(ASP.NET Web API)本身与主机无关,因此将来有人可能会实现不同的主机,主机将以不同的方式显示该属性(远程端点)。 不幸的是,如果您使用 Owin 自托管,这将不起作用(推荐用于 Web API 2)。如果有的话需要另一个...【参考方案2】:

此解决方案还涵盖使用 Owin 自托管的 Web API。部分来自here。

您可以在 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 种情况加载库,而在运行时您实际上只使用其中一种。正如here 建议的那样,这可以通过使用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();
    

【讨论】:

这个解决方案应该使用这个命名空间“System.Net.Http”来工作。由于这是 Assembly System.Web.Http.dll 上的类名,v5.2.2.0。 @WagnerBertolini,你是对的,你需要using System.Net.Http; 行,因为你正在扩展HttpRequestMessage。除非您在 System.Net.Http 命名空间中定义您的扩展,否则这是非常值得怀疑的。但不确定它是否必不可少,因为它会被任何 IDE 或生产力工具自动添加。你怎么看? 我急于在这里完成一项工作,我花了 20 多分钟才看到构建错误发生了什么。我只是复制了代码并为它创建了一个类,在编译时它没有显示方法,当我在 VS 上使用“转到定义”时,它把我带到了我的班级,我一直不明白发生了什么,直到我找到了另一个班级。扩展是一个相当新的功能,并不是一直都在使用,因为,我认为节省这段时间是个好主意。 使用 OWIN 时,您可以使用 OwinHttpRequestMessageExtensions 来获取 OWIN 上下文,例如:request.GetOwinContext().Request.RemoteIpAddress 其实应该是 var ctx = request.Properties[MsHttpContext] as HttpContextWrapper; E如果你转换,你不需要检查 null 因为如果转换失败,你会得到一个异常【参考方案3】:

如果您真的想要一个单行且不打算自托管 Web API:

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

【讨论】:

【参考方案4】:

以上答案需要引用 System.Web 才能将属性转换为 HttpContext 或 HttpContextWrapper。如果您不想要引用,您可以使用动态获取 ip:

var host = ((dynamic)request.Properties["MS_HttpContext"]).Request.UserHostAddress;

【讨论】:

【参考方案5】:

当服务器位于代理或负载平衡器后面时,标记的解决方案将返回代理的内部 IP 地址。在我们的例子中,我们的生产环境使用负载均衡器,而我们的开发和测试环境没有,所以我修改了标记的解决方案,以在相同的代码中适应这两种情况。

public string GetSourceIp(HttpRequestMessage httpRequestMessage)
    
        string result = string.Empty;

        // Detect X-Forwarded-For header
        if (httpRequestMessage.Headers.TryGetValues("X-Forwarded-For", out IEnumerable<string> headerValues))
        
            result = headerValues.FirstOrDefault();
        
        // Web-hosting
        else if (httpRequestMessage.Properties.ContainsKey("MS_HttpContext"))
        
            result = ((HttpContextWrapper)httpRequestMessage.Properties["MS_HttpContext"]).Request.UserHostAddress;
        
        // Self-hosting
        else if (httpRequestMessage.Properties.ContainsKey(RemoteEndpointMessageProperty.Name))
        
            RemoteEndpointMessageProperty prop;
            prop = (RemoteEndpointMessageProperty)httpRequestMessage.Properties[RemoteEndpointMessageProperty.Name];
            result = prop.Address;
        

        return result;
    

【讨论】:

正是我想要的。大多数值得生产的应用程序都在重写标头的代理后面,这是处理该问题的方法..【参考方案6】:

carlosfigueira 提供的解决方案有效,但类型安全的单行更好:添加 using System.Web 然后在您的操作方法中访问 HttpContext.Current.Request.UserHostAddress

【讨论】:

-1 这不能在 web api 中被信任,因为 HttpContext.Current 没有在整个任务管道中正确持久化;因为所有请求处理都是异步的。 HttpContext.Current 在编写 Web API 代码时几乎总是应该避免的。 @Andras,我想更详细地了解为什么使用 HttpContext.Current 不好,你知道任何有价值的资源吗? 嗨@CuongLe;事实上 - 虽然线程问题可能是一个问题(尽管如果SynchronizationContext 在任务之间正确流动,则并不总是直接的);最大的问题是您的服务代码是否可能是自托管的(例如测试)-HttpContext.Current 是一个纯粹的 Asp.Net 构造,并且在您自托管时不存在。 类型安全并不是一切。如果从线程(例如任务等待器,在现代 Web API 代码中非常常见)或自托管上下文中使用,此代码将引发难以调试的 NullReferenceException。至少大多数其他答案只会返回null

以上是关于获取远程主机的IP地址的主要内容,如果未能解决你的问题,请参考以下文章

配置LINUX主机IP地址并修改hosts文件和远程桌面

使用PowerShell脚本获取远程主机的IP配置信息

cobbler自动安装系统

.NET Socket.Connect 将主机名解析为 IP 地址

检查 ip:port 在远程主机上是不是打开

发送公钥到多台远程主机