Answers:
可以这样做,但不是很容易发现-您需要使用传入请求中的属性包,而需要访问的属性取决于您是在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;
}
RemoteEndpointMessageProperty
在类System.ServiceModel.Channels
的命名空间,System.ServiceModel.dll
组装?那不是属于WCF的程序集吗?
该解决方案还涵盖了使用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.dllRemoteEndpointMessageProperty
-System.ServiceModel.dllOwinContext
-Microsoft.Owin.dll(如果使用Owin包,则已经有了)此解决方案的一个小问题是,当您在运行时实际上仅使用其中的一种情况时,必须为所有3种情况加载库。作为建议在这里,这可以通过使用可以克服dynamic
变量。您也可以将GetClientIpAddress
method 编写为的扩展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();
}
}
using System.Net.Http;
因为您正在扩展,所以需要一行HttpRequestMessage
。除非您要在System.Net.Http
命名空间中定义扩展名,否则这是非常有问题的。但是不确定它是否必不可少,因为它将由任何IDE或生产力工具自动添加。你怎么看?
carlosfigueira提供的解决方案有效,但是类型安全的单行代码更好:在操作方法中添加“ using System.Web
然后访问” HttpContext.Current.Request.UserHostAddress
。
HttpContext.Current
在整个任务管道中无法正确持久保存;因为所有请求处理都是异步的。HttpContext.Current
编写Web API代码时应几乎始终避免使用。
SynchronizationContext
在任务之间正确流动,则可能并非总是如此);最大的问题是您的服务代码是否有可能是自托管的(例如,测试)- HttpContext.Current
是纯Asp.Net结构,自托管时不存在。
NullReferenceException
如果从线程(例如,任务等待者,在现代Web API代码中非常常见)或在自托管上下文中使用,则此代码将引发难以调试的事件。至少大多数其他答案只会返回null
。