I am testing a new load balanced staging site and the https is set up at the load balancer level, not at the site level. Also, this site will be always https so i don't need remote require https attributes etc. The url displays https but it is not available in my code. I have a few issues due to this reason
Request.Url.Scheme is always http:
public static string GetProtocol()
{
var protocol = "http";
if (HttpContext.Current != null && HttpContext.Current.Request != null)
{
protocol = HttpContext.Current.Request.Url.Scheme;
}
return protocol;
}
Same thing with this base url, protocol is http
public static string GetBaseUrl()
{
var baseUrl = String.Empty;
if (HttpContext.Current == null || HttpContext.Current.Request == null || String.IsNullOrWhiteSpace(HttpRuntime.AppDomainAppPath)) return baseUrl;
var request = HttpContext.Current.Request;
var appUrl = HttpRuntime.AppDomainAppVirtualPath;
baseUrl = string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Authority, appUrl);
if (!string.IsNullOrWhiteSpace(baseUrl) && !baseUrl.EndsWith("/"))
baseUrl = String.Format("{0}/", baseUrl);
return baseUrl;
}
Now the biggest issue is referencing js files and google fonts referenced in the style sheets. I am using // here without http or https but these are treated as http and i see mixed content blocked message in FireBug.
How can i overcome this issue?
As you've said HTTPS termination is done at load balancer level ("https is set up at the load balancer level") which means original scheme may not come to the site depending on loadbalancer configuration.
It looks like in your case LB is configured to talk to site over HTTP all the time. So your site will never see original scheme on HttpContext.Request.RawUrl (or similar properties).
Fix: usually when LB, proxy or CDN configured such way there are additional headers that specify original scheme and likely other incoming request parameters like full url, client's IP which will be not directly visible to the site behind such proxying device.
I override the ServerVariables to convince MVC it really is communicating through HTTPS and also expose the user's IP address. This is using the X-Forwarded-For and X-Forwarded-Proto HTTP headers being set by your load balancer.
Note that you should only use this if you're really sure these headers are under your control, otherwise clients might inject values of their liking.
public sealed class HttpOverrides : IHttpModule
{
void IHttpModule.Init(HttpApplication app)
{
app.BeginRequest += OnBeginRequest;
}
private void OnBeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
string forwardedFor = app.Context.Request.Headers["X-Forwarded-For"]?.Split(new char[] { ',' }).FirstOrDefault();
if (forwardedFor != null)
{
app.Context.Request.ServerVariables["REMOTE_ADDR"] = forwardedFor;
app.Context.Request.ServerVariables["REMOTE_HOST"] = forwardedFor;
}
string forwardedProto = app.Context.Request.Headers["X-Forwarded-Proto"];
if (forwardedProto == "https")
{
app.Context.Request.ServerVariables["HTTPS"] = "on";
app.Context.Request.ServerVariables["SERVER_PORT"] = "443";
app.Context.Request.ServerVariables["SERVER_PORT_SECURE"] = "1";
}
}
void IHttpModule.Dispose()
{
}
}
And in Web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="HttpOverrides" type="Namespace.HttpOverrides" preCondition="integratedMode" />
</modules>
</system.webServer>
I know this is an old question, but after encountering the same problem, I did discover that if I look into the UrlReferrer property of the HttpRequest object, the values will reflect what was actually in the client browser's address bar.
So for example, with UrlReferrer I got:
Request.UrlReferrer.Scheme == "https"
Request.UrlReferrer.Port == 443
But for the same request, with the Url property I got the following:
Request.Url.Scheme == "http"
Request.Url.Port == 80
According to https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer
When HTTPS requests are proxied over HTTP, the original scheme (HTTPS)
may be lost and must be forwarded in a header.
In Asp.Net Core I found ( not sure if it works for all scenarios) that even if request.Scheme is misleadingly shows “http” for original “https”, request.IsHttps property is more reliable.
I am using the following code
//Scheme may return http for https
var scheme = request.Scheme;
if(request.IsHttps) scheme= scheme.EnsureEndsWith("S");
//General string extension
public static string EnsureEndsWith(this string str, string sEndValue, bool ignoreCase = true)
{
if (!str.EndsWith(sEndValue, CurrentCultureComparison(ignoreCase)))
{
str = str + sEndValue;
}
return str;
}
Related
We have a website and some of the pages are using https and those pages kept in magic folder.
https enabling and port number is configured in web.config for a site.
However if the user trying to access magic folder contents using http, we need to redirect back to https and vise versa
Ex Case 1: Working for http to https
http://mysite/magic-look to https://mysite/magic-look
Here, we used
<urlMappings>
<add url="~/magic-look" mappedUrl="~/magic/look.aspx"/>
<add url="~/help" mappedUrl="~/Help/default.aspx"/>
In Global.asax
protected void Application_BeginRequest(object sender, EventArgs e)
{
string url = HttpContext.Current.Request.Url.AbsoluteUri;
var secPort = String.IsNullOrEmpty(ConfigurationManager.AppSettings["securePort"]) ? 0 : Convert.ToInt32(ConfigurationManager.AppSettings["securePort"]);
var secProtocolEnabled = String.IsNullOrEmpty(ConfigurationManager.AppSettings["useSecure"]) ? false : true;
bool isSecureUrl = (url.IndexOf("/magic/", StringComparison.OrdinalIgnoreCase) >= 0) ? true : false;
if (url.IndexOf(".aspx", StringComparison.OrdinalIgnoreCase) >= 0)
{
url = url.Replace(":" + secPort, "");
if (isSecureUrl && secProtocolEnabled)
{
if (HttpContext.Current.Request.Url.Port != secPort)
{
//change .aspx page back to original SEO friendly URL and redirect
url = url.Replace(HttpContext.Current.Request.Url.AbsolutePath, HttpContext.Current.Request.RawUrl);
HttpContext.Current.Response.Redirect(Regex.Replace(url, "http", "https", RegexOptions.IgnoreCase));
}
}
else
{
if (HttpContext.Current.Request.Url.Port == secPort && !isSecureUrl)
{
//cause infinite loop
url = url.Replace(HttpContext.Current.Request.Url.AbsolutePath, HttpContext.Current.Request.RawUrl);
var targetUrl = Regex.Replace(url, "https", "http", RegexOptions.IgnoreCase);
HttpContext.Current.Response.Redirect(targetUrl);
}
}
}
}
Non https page accessed using Https, Not working, infinite loop
ISSUE: Not working for https to http
https://mysite/help to http://mysite/help
It gives infinite loop.. keep redirecting to *https://mysite/help*
https://mysite/help --> 302 Found
https://mysite/Help --> 302 Found
https://mysite/Help --> 302 Found
https://mysite/Help --> 302 Found .............
UPDATE:
If it remove this, it works fine.
url = url.Replace(HttpContext.Current.Request.Url.AbsolutePath,
HttpContext.Current.Request.RawUrl);
But 3 requests instead of 2
https://mysite/help --> 302 Found
https://mysite/Help/Default.aspx--> 302 Found
http://mysite/Help/Default.aspx--> 200 OK
However i want SEO friendly url like http://mysite/Help/
UPDATE 2: ROOT CAUSE:
Whenever the url is https://../something and redirecting to http://../something is always making request https://../something
I just re-factored your code and used string.Format method to build url
private static string GetMyUrl(string securePort, string useSecure, Uri uri)
{
int secPort;
if (!int.TryParse(securePort, out secPort)) secPort = 0;
bool secProtocolEnabled = !string.IsNullOrWhiteSpace(useSecure);
bool shouldBeSecureUrl = uri.AbsolutePath.Contains("/magic/");
if (!uri.AbsolutePath.EndsWith(".aspx")) return uri.AbsoluteUri;
bool forceSecure = (shouldBeSecureUrl && secProtocolEnabled);
string url = string.Format("{0}://{1}{2}{3}",
forceSecure
? "https"
: "http",
uri.Host,
forceSecure && secPort != 0
? string.Format(":{0}", secPort)
: string.Empty,
uri.PathAndQuery);
return url;
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
string url = GetMyUrl(ConfigurationManager.AppSettings["securePort"],
ConfigurationManager.AppSettings["useSecure"], HttpContext.Current.Request.Url);
if (HttpContext.Current.Request.Url.AbsoluteUri != url) HttpContext.Current.Response.Redirect(url);
}
You can test your addresses. Samples...
Console.WriteLine(GetMyUrl("8484","t", new Uri("https://www.contoso.com/catalog/shownew.aspx?date=today")));
Console.WriteLine(GetMyUrl("8484","t", new Uri("https://www.contoso.com:8484/magic/shownew.aspx?date=today")));
Console.WriteLine(GetMyUrl("8484","t", new Uri("http://www.contoso.com:8080/magic/shownew.aspx?date=today")));
Console.WriteLine(GetMyUrl("","t", new Uri("https://www.contoso.com/catalog/shownew.aspx?date=today")));
Console.WriteLine(GetMyUrl("8484","", new Uri("https://www.contoso.com/catalog/shownew.aspx?date=today")));
Finally found that we have url rewrite enabled, so just added config entry, works for all scenario
<configSections>
<section name="URLRewriteConfiguration"
type="MyAssembly.Routing.URLRewriteConfiguration, MyAssembly.Routing,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</configSections>
<URLRewriteConfiguration>
<Routes>
<add Pattern="/help/default.aspx" PermanentRedirect="true" Replacement="/help"/>
and removed
url = url.Replace(HttpContext.Current.Request.Url.AbsolutePath, HttpContext.Current.Request.RawUrl);
I need to do URL rewriting in such a way that
if my request is abc.domain.com, then request should be processed such as
domain.com/default.aspx?cid=abc
for example, if i give .domain.com, request should be assumed as domain.com/default.aspx?cid=
it is not mandatory that i need to give abc alone the subdomain can be any.. but my request should be processed assuming it as querystring value.
My domain is in Shared hosting...
can anyone throw light in this..
The Subdomain must be created and configure on DNS server and on IIS.
After you have setup your site to accept that subdomains, you may direct use the RewritePath to map a subdomain to a specific file with different parameters.
Starting from Application_BeginRequest you check and find the subdomain and rewrite the path as:
protected void Application_BeginRequest(Object sender, EventArgs e)
{
var SubDomain = GetSubDomain(HttpContext.Current.Request.Host);
// this is a simple example, you can place your variables base on subdomain
if(!String.IsNullOrEmpty(SubDomain))
RewritePath(HttpContext.Current.Request.Path + SubDomain + "/", false);
}
// from : http://madskristensen.net/post/Retrieve-the-subdomain-from-a-URL-in-C.aspx
private static string GetSubDomain(Uri url)
{
string host = url.Host;
if (host.Split('.').Length > 1)
{
int index = host.IndexOf(".");
return host.Substring(0, index);
}
return null;
}
Similar:
How to remap all the request to a specific domain to subdirectory in ASP.NET
Redirect Web Page Requests to a Default Sub Folder
Retrieve the subdomain from a URL in C#
I have a web application (hosted in IIS) that talks to a Windows service. The Windows service is using the ASP.Net MVC Web API (self-hosted), and so can be communicated with over http using JSON. The web application is configured to do impersonation, the idea being that the user who makes the request to the web application should be the user that the web application uses to make the request to the service. The structure looks like this:
(The user highlighted in red is the user being referred to in the examples below.)
The web application makes requests to the Windows service using an HttpClient:
var httpClient = new HttpClient(new HttpClientHandler()
{
UseDefaultCredentials = true
});
httpClient.GetStringAsync("http://localhost/some/endpoint/");
This makes the request to the Windows service, but does not pass the credentials over correctly (the service reports the user as IIS APPPOOL\ASP.NET 4.0). This is not what I want to happen.
If I change the above code to use a WebClient instead, the credentials of the user are passed correctly:
WebClient c = new WebClient
{
UseDefaultCredentials = true
};
c.DownloadStringAsync(new Uri("http://localhost/some/endpoint/"));
With the above code, the service reports the user as the user who made the request to the web application.
What am I doing wrong with the HttpClient implementation that is causing it to not pass the credentials correctly (or is it a bug with the HttpClient)?
The reason I want to use the HttpClient is that it has an async API that works well with Tasks, whereas the WebClient's asyc API needs to be handled with events.
You can configure HttpClient to automatically pass credentials like this:
var myClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
I was also having this same problem. I developed a synchronous solution thanks to the research done by #tpeczek in the following SO article: Unable to authenticate to ASP.NET Web Api service with HttpClient
My solution uses a WebClient, which as you correctly noted passes the credentials without issue. The reason HttpClient doesn't work is because of Windows security disabling the ability to create new threads under an impersonated account (see SO article above.) HttpClient creates new threads via the Task Factory thus causing the error. WebClient on the other hand, runs synchronously on the same thread thereby bypassing the rule and forwarding its credentials.
Although the code works, the downside is that it will not work async.
var wi = (System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
Note: Requires NuGet package: Newtonsoft.Json, which is the same JSON serializer WebAPI uses.
What you are trying to do is get NTLM to forward the identity on to the next server, which it cannot do - it can only do impersonation which only gives you access to local resources. It won't let you cross a machine boundary. Kerberos authentication supports delegation (what you need) by using tickets, and the ticket can be forwarded on when all servers and applications in the chain are correctly configured and Kerberos is set up correctly on the domain.
So, in short you need to switch from using NTLM to Kerberos.
For more on Windows Authentication options available to you and how they work start at:
http://msdn.microsoft.com/en-us/library/ff647076.aspx
OK, so thanks to all of the contributors above. I am using .NET 4.6 and we also had the same issue. I spent time debugging System.Net.Http, specifically the HttpClientHandler, and found the following:
if (ExecutionContext.IsFlowSuppressed())
{
IWebProxy webProxy = (IWebProxy) null;
if (this.useProxy)
webProxy = this.proxy ?? WebRequest.DefaultWebProxy;
if (this.UseDefaultCredentials || this.Credentials != null || webProxy != null && webProxy.Credentials != null)
this.SafeCaptureIdenity(state);
}
So after assessing that the ExecutionContext.IsFlowSuppressed() might have been the culprit, I wrapped our Impersonation code as follows:
using (((WindowsIdentity)ExecutionContext.Current.Identity).Impersonate())
using (System.Threading.ExecutionContext.SuppressFlow())
{
// HttpClient code goes here!
}
The code inside of SafeCaptureIdenity (not my spelling mistake), grabs WindowsIdentity.Current() which is our impersonated identity. This is being picked up because we are now suppressing flow. Because of the using/dispose this is reset after invocation.
It now seems to work for us, phew!
In .NET Core, I managed to get a System.Net.Http.HttpClient with UseDefaultCredentials = true to pass through the authenticated user's Windows credentials to a back end service by using WindowsIdentity.RunImpersonated.
HttpClient client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true } );
HttpResponseMessage response = null;
if (identity is WindowsIdentity windowsIdentity)
{
await WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, async () =>
{
var request = new HttpRequestMessage(HttpMethod.Get, url)
response = await client.SendAsync(request);
});
}
It worked for me after I set up a user with internet access in the Windows service.
In my code:
HttpClientHandler handler = new HttpClientHandler();
handler.Proxy = System.Net.WebRequest.DefaultWebProxy;
handler.Proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
.....
HttpClient httpClient = new HttpClient(handler)
....
Ok so I took Joshoun code and made it generic. I am not sure if I should implement singleton pattern on SynchronousPost class. Maybe someone more knowledgeble can help.
Implementation
//I assume you have your own concrete type. In my case I have am using code first with a class called FileCategory
FileCategory x = new FileCategory { CategoryName = "Some Bs"};
SynchronousPost<FileCategory>test= new SynchronousPost<FileCategory>();
test.PostEntity(x, "/api/ApiFileCategories");
Generic Class here. You can pass any type
public class SynchronousPost<T>where T :class
{
public SynchronousPost()
{
Client = new WebClient { UseDefaultCredentials = true };
}
public void PostEntity(T PostThis,string ApiControllerName)//The ApiController name should be "/api/MyName/"
{
//this just determines the root url.
Client.BaseAddress = string.Format(
(
System.Web.HttpContext.Current.Request.Url.Port != 80) ? "{0}://{1}:{2}" : "{0}://{1}",
System.Web.HttpContext.Current.Request.Url.Scheme,
System.Web.HttpContext.Current.Request.Url.Host,
System.Web.HttpContext.Current.Request.Url.Port
);
Client.Headers.Add(HttpRequestHeader.ContentType, "application/json;charset=utf-8");
Client.UploadData(
ApiControllerName, "Post",
Encoding.UTF8.GetBytes
(
JsonConvert.SerializeObject(PostThis)
)
);
}
private WebClient Client { get; set; }
}
My Api classs looks like this, if you are curious
public class ApiFileCategoriesController : ApiBaseController
{
public ApiFileCategoriesController(IMshIntranetUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
public IEnumerable<FileCategory> GetFiles()
{
return UnitOfWork.FileCategories.GetAll().OrderBy(x=>x.CategoryName);
}
public FileCategory GetFile(int id)
{
return UnitOfWork.FileCategories.GetById(id);
}
//Post api/ApileFileCategories
public HttpResponseMessage Post(FileCategory fileCategory)
{
UnitOfWork.FileCategories.Add(fileCategory);
UnitOfWork.Commit();
return new HttpResponseMessage();
}
}
I am using ninject, and repo pattern with unit of work. Anyways, the generic class above really helps.
Set identity's impersonation to true and validateIntegratedModeConfiguration to false in web.config
<configuration>
<system.web>
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
<identity impersonate="true"/>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" ></validation>
</system.webServer>
</configuration>
string url = "https://www..com";
System.Windows.Forms.WebBrowser webBrowser = new System.Windows.Forms.WebBrowser();
this.Controls.Add(webBrowser);
webBrowser.ScriptErrorsSuppressed = true;
webBrowser.Navigate(new Uri(url));
var webRequest = WebRequest.Create(url);
webRequest.Headers["Authorization"] = "Basic" + Convert.ToBase64String(Encoding.Default.GetBytes(Program.username + ";" + Program.password));
webRequest.Method = "POST";
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?
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;
}
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();
}
}
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;
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;
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;
}
Based on Asaff Belfer's answer, here's a couple of basic methods that I used to deploy this to an Azure Function (serverless / consumption plan) to obtain both the client IP and the client user agent. Also included a bit that is supposed to work in APIM, but have not tested that part yet. That information about APIM can be found at Stefano Demiliani's blog.
NOTE: these will return "(not available)" for local / self hosting. Someone can fix these to include self hosting, but I could not use those bits as the required Nuget packages from Asaff's answer (and others here) don't appear to work on the target framework I'm using (.NET 6.0).
public static string GetSourceIp(HttpRequestMessage httpRequestMessage)
{
string result = "(not available)";
// Detect X-Forwarded-For header
if (httpRequestMessage.Headers.TryGetValues("X-Forwarded-For", out IEnumerable<string> headerValues))
{
result = headerValues.FirstOrDefault();
}
//for use with APIM, see https://demiliani.com/2022/07/11/azure-functions-getting-the-client-ip-address
if (httpRequestMessage.Headers.TryGetValues("X-Forwarded-Client-Ip", out IEnumerable<string> headerValues2))
{
result = headerValues2.FirstOrDefault();
}
return result;
}
public static string GetClientUserAgent(HttpRequestMessage httpRequestMessage)
{
string result = "(not available)";
// Detect user-agent header
if (httpRequestMessage.Headers.TryGetValues("user-agent", out IEnumerable<string> headerValues))
{
result = string.Join(", ", headerValues);
}
return result;
}
Usage would be as follows:
string clientUserAgent = GetClientUserAgent(httpRequestMessage);
string clientIP = GetSourceIp(httpRequestMessage);
Hosted locally on my dev box using a Chrome browser, these returned:
User IP Address: (not available)
User Client: Mozilla/5.0, (Windows NT 10.0; Win64; x64), AppleWebKit/537.36, (KHTML, like Gecko), Chrome/108.0.0.0, Safari/537.36
Hosted in my serverless function in Azure commercial, using a Chrome browser, these returned:
User IP Address: 999.999.999.999:12345
(Of course, this is not a real IP address. I assume the 12345 part is a port number.)
User Client: Mozilla/5.0, (Windows NT 10.0; Win64; x64), AppleWebKit/537.36, (KHTML, like Gecko), Chrome/108.0.0.0, Safari/537.36
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.
My asp.net application will be distributed to different users. It should retrieve the domain and http type of a link automatically.
For example:
http://abc.com/test.aspx
https://abc.com/test2.aspx
I need a method to retrieve either: http://abc.com and https://abc.com from those links automatically.
How can I do that?
thanks in advance.
You can construct a Uri from your full url and build your new url.
public string GetRootUrl(string url)
{
Uri uri = new Uri(str);
return string.Format("{0}://{1}", uri.Scheme, uri.Authority);
}
However, you can use uri.Host, but in case of any deployment where you will host your application on a port like http://abc.com:8087 then uri.Host will just return abc.com and uri.Authority will return abc.com:8087. Default port 80 will be ignored by uri.Authority and work like uri.Host.
string getSchemeAndHost(string str)
{
var uri = new Uri(str);
return uri.Scheme + "://" + uri.Host;
}