Get the IP address of the remote host

C#asp.net Web-Api

C# Problem Overview


In ASP.NET there is a System.Web.HttpRequest class, which contains ServerVariables property which can provide us the IP address from REMOTE_ADDR property value.

However, I could not find a similar way to get the IP address of the remote host from ASP.NET Web API.

How can I get the IP address of the remote host that is making the request?

C# Solutions


Solution 1 - C#

It's possible to do that, but not very discoverable - you need to use the property bag from the incoming request, and the property you need to access depends on whether you're using the Web API under IIS (webhosted) or self-hosted. The code below shows how this can be done.

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

Solution 2 - C#

This solution also covers Web API self-hosted using Owin. Partially from here.

You can create a private method in you ApiController that will return remote IP address no matter how you host your Web 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;
 }

References required:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (you will have it already if you use Owin package)

A little problem with this solution is that you have to load libraries for all 3 cases when you will actually be using only one of them during runtime. As suggested here, this can be overcome by using dynamic variables. You can also write GetClientIpAddress method as an extension for 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;
    }
}

Now you can use it like this:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}

Solution 3 - C#

If you really want a one-liner and don't plan to self-host Web API:

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

Solution 4 - C#

Above answers require a reference to System.Web to be able to cast the property to HttpContext or HttpContextWrapper. If you don't want the reference, you are able to get the ip using a dynamic:

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

Solution 5 - C#

When the server is behind a proxy or a load balancer the marked solution will return the internal IP address of the proxy. In our case, our production environment is using a load balancer and our development and test environments are not so I've amended the marked solution to fit both cases in the same code.

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

Solution 6 - C#

The solution provided by carlosfigueira works, but type-safe one-liners are better: Add a using System.Web then access HttpContext.Current.Request.UserHostAddress in your action method.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionpaulius_lView Question on Stackoverflow
Solution 1 - C#carlosfigueiraView Answer on Stackoverflow
Solution 2 - C#Nikolai SamteladzeView Answer on Stackoverflow
Solution 3 - C#zacharydlView Answer on Stackoverflow
Solution 4 - C#Robin van der KnaapView Answer on Stackoverflow
Solution 5 - C#Asaff BelferView Answer on Stackoverflow
Solution 6 - C#Warren RumakView Answer on Stackoverflow