Try your search with a different keyword or use * as a wildcard.
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Nop.Core.Http;
namespace Nop.Core;
/// <summary>
/// Represents a web helper
/// </summary>
public partial class WebHelper : IWebHelper
{
#region Fields
protected readonly IActionContextAccessor _actionContextAccessor;
protected readonly IHostApplicationLifetime _hostApplicationLifetime;
protected readonly IHttpContextAccessor _httpContextAccessor;
protected readonly IUrlHelperFactory _urlHelperFactory;
protected readonly Lazy<IStoreContext> _storeContext;
#endregion
#region Ctor
public WebHelper(IActionContextAccessor actionContextAccessor,
IHostApplicationLifetime hostApplicationLifetime,
IHttpContextAccessor httpContextAccessor,
IUrlHelperFactory urlHelperFactory,
Lazy<IStoreContext> storeContext)
{
_actionContextAccessor = actionContextAccessor;
_hostApplicationLifetime = hostApplicationLifetime;
_httpContextAccessor = httpContextAccessor;
_urlHelperFactory = urlHelperFactory;
_storeContext = storeContext;
}
#endregion
#region Utilities
/// <summary>
/// Check whether current HTTP request is available
/// </summary>
/// <returns>True if available; otherwise false</returns>
protected virtual bool IsRequestAvailable()
{
if (_httpContextAccessor?.HttpContext == null)
return false;
try
{
if (_httpContextAccessor.HttpContext?.Request == null)
return false;
}
catch (Exception)
{
return false;
}
return true;
}
/// <summary>
/// Is IP address specified
/// </summary>
/// <param name="address">IP address</param>
/// <returns>Result</returns>
protected virtual bool IsIpAddressSet(IPAddress address)
{
var rez = address != null && address.ToString() != IPAddress.IPv6Loopback.ToString();
return rez;
}
#endregion
#region Methods
/// <summary>
/// Get URL referrer if exists
/// </summary>
/// <returns>URL referrer</returns>
public virtual string GetUrlReferrer()
{
if (!IsRequestAvailable())
return string.Empty;
//URL referrer is null in some case (for example, in IE 8)
return _httpContextAccessor.HttpContext.Request.Headers[HeaderNames.Referer];
}
/// <summary>
/// Get IP address from HTTP context
/// </summary>
/// <returns>String of IP address</returns>
public virtual string GetCurrentIpAddress()
{
if (!IsRequestAvailable() || _httpContextAccessor.HttpContext!.Connection.RemoteIpAddress is not { } remoteIp)
return string.Empty;
return (remoteIp.Equals(IPAddress.IPv6Loopback) ? IPAddress.Loopback : remoteIp).ToString();
}
/// <summary>
/// Gets this page URL
/// </summary>
/// <param name="includeQueryString">Value indicating whether to include query strings</param>
/// <param name="useSsl">Value indicating whether to get SSL secured page URL. Pass null to determine automatically</param>
/// <param name="lowercaseUrl">Value indicating whether to lowercase URL</param>
/// <returns>Page URL</returns>
public virtual string GetThisPageUrl(bool includeQueryString, bool? useSsl = null, bool lowercaseUrl = false)
{
if (!IsRequestAvailable())
return string.Empty;
//get store location
var storeLocation = GetStoreLocation(useSsl ?? IsCurrentConnectionSecured());
//add local path to the URL
var pageUrl = $"{storeLocation.TrimEnd('/')}{_httpContextAccessor.HttpContext.Request.Path}";
//add query string to the URL
if (includeQueryString)
pageUrl = $"{pageUrl}{_httpContextAccessor.HttpContext.Request.QueryString}";
//whether to convert the URL to lower case
if (lowercaseUrl)
pageUrl = pageUrl.ToLowerInvariant();
return pageUrl;
}
/// <summary>
/// Gets a value indicating whether current connection is secured
/// </summary>
/// <returns>True if it's secured, otherwise false</returns>
public virtual bool IsCurrentConnectionSecured()
{
if (!IsRequestAvailable())
return false;
return _httpContextAccessor.HttpContext.Request.IsHttps;
}
/// <summary>
/// Gets store host location
/// </summary>
/// <param name="useSsl">Whether to get SSL secured URL</param>
/// <returns>Store host location</returns>
public virtual string GetStoreHost(bool useSsl)
{
if (!IsRequestAvailable())
return string.Empty;
//try to get host from the request HOST header
var hostHeader = _httpContextAccessor.HttpContext.Request.Headers[HeaderNames.Host];
if (StringValues.IsNullOrEmpty(hostHeader))
return string.Empty;
//add scheme to the URL
var storeHost = $"{(useSsl ? Uri.UriSchemeHttps : Uri.UriSchemeHttp)}{Uri.SchemeDelimiter}{hostHeader.FirstOrDefault()}";
//ensure that host is ended with slash
storeHost = $"{storeHost.TrimEnd('/')}/";
return storeHost;
}
/// <summary>
/// Gets store location
/// </summary>
/// <param name="useSsl">Whether to get SSL secured URL; pass null to determine automatically</param>
/// <returns>Store location</returns>
public virtual string GetStoreLocation(bool? useSsl = null)
{
var storeLocation = string.Empty;
//get store host
var storeHost = GetStoreHost(useSsl ?? IsCurrentConnectionSecured());
if (!string.IsNullOrEmpty(storeHost))
{
//add application path base if exists
storeLocation = IsRequestAvailable() ? $"{storeHost.TrimEnd('/')}{_httpContextAccessor.HttpContext.Request.PathBase}" : storeHost;
}
//if host is empty (it is possible only when HttpContext is not available), use URL of a store entity configured in admin area
if (string.IsNullOrEmpty(storeHost))
storeLocation = _storeContext.Value.GetCurrentStore()?.Url
?? throw new Exception("Current store cannot be loaded");
//ensure that URL is ended with slash
storeLocation = $"{storeLocation.TrimEnd('/')}/";
return storeLocation;
}
/// <summary>
/// Returns true if the requested resource is one of the typical resources that needn't be processed by the cms engine.
/// </summary>
/// <returns>True if the request targets a static resource file.</returns>
public virtual bool IsStaticResource()
{
if (!IsRequestAvailable())
return false;
string path = _httpContextAccessor.HttpContext.Request.Path;
//a little workaround. FileExtensionContentTypeProvider contains most of static file extensions. So we can use it
//source: https://github.com/aspnet/StaticFiles/blob/dev/src/Microsoft.AspNetCore.StaticFiles/FileExtensionContentTypeProvider.cs
//if it can return content type, then it's a static file
var contentTypeProvider = new FileExtensionContentTypeProvider();
return contentTypeProvider.TryGetContentType(path, out var _);
}
/// <summary>
/// Modify query string of the URL
/// </summary>
/// <param name="url">Url to modify</param>
/// <param name="key">Query parameter key to add</param>
/// <param name="values">Query parameter values to add</param>
/// <returns>New URL with passed query parameter</returns>
public virtual string ModifyQueryString(string url, string key, params string[] values)
{
if (string.IsNullOrEmpty(url))
return string.Empty;
if (string.IsNullOrEmpty(key))
return url;
//prepare URI object
var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
var isLocalUrl = urlHelper.IsLocalUrl(url);
var uriStr = url;
if (isLocalUrl)
{
var pathBase = _httpContextAccessor.HttpContext.Request.PathBase;
uriStr = $"{GetStoreLocation().TrimEnd('/')}{(url.StartsWith(pathBase) ? url.Replace(pathBase, "") : url)}";
}
var uri = new Uri(uriStr, UriKind.Absolute);
//get current query parameters
var queryParameters = QueryHelpers.ParseQuery(uri.Query);
//and add passed one
queryParameters[key] = string.Join(",", values);
//add only first value
//two the same query parameters? theoretically it's not possible.
//but MVC has some ugly implementation for checkboxes and we can have two values
//find more info here: http://www.mindstorminteractive.com/topics/jquery-fix-asp-net-mvc-checkbox-truefalse-value/
//we do this validation just to ensure that the first one is not overridden
var queryBuilder = new QueryBuilder(queryParameters
.ToDictionary(parameter => parameter.Key, parameter => parameter.Value.FirstOrDefault()?.ToString() ?? string.Empty));
//create new URL with passed query parameters
url = $"{(isLocalUrl ? uri.LocalPath : uri.GetLeftPart(UriPartial.Path))}{queryBuilder.ToQueryString()}{uri.Fragment}";
return url;
}
/// <summary>
/// Remove query parameter from the URL
/// </summary>
/// <param name="url">Url to modify</param>
/// <param name="key">Query parameter key to remove</param>
/// <param name="value">Query parameter value to remove; pass null to remove all query parameters with the specified key</param>
/// <returns>New URL without passed query parameter</returns>
public virtual string RemoveQueryString(string url, string key, string value = null)
{
if (string.IsNullOrEmpty(url))
return string.Empty;
if (string.IsNullOrEmpty(key))
return url;
//prepare URI object
var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
var isLocalUrl = urlHelper.IsLocalUrl(url);
var uri = new Uri(isLocalUrl ? $"{GetStoreLocation().TrimEnd('/')}{url}" : url, UriKind.Absolute);
//get current query parameters
var queryParameters = QueryHelpers.ParseQuery(uri.Query)
.SelectMany(parameter => parameter.Value, (parameter, queryValue) => new KeyValuePair<string, string>(parameter.Key, queryValue))
.ToList();
if (!string.IsNullOrEmpty(value))
{
//remove a specific query parameter value if it's passed
queryParameters.RemoveAll(parameter => parameter.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)
&& parameter.Value.Equals(value, StringComparison.InvariantCultureIgnoreCase));
}
else
{
//or remove query parameter by the key
queryParameters.RemoveAll(parameter => parameter.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));
}
var queryBuilder = new QueryBuilder(queryParameters);
//create new URL without passed query parameters
url = $"{(isLocalUrl ? uri.LocalPath : uri.GetLeftPart(UriPartial.Path))}{queryBuilder.ToQueryString()}{uri.Fragment}";
return url;
}
/// <summary>
/// Gets query string value by name
/// </summary>
/// <typeparam name="T">Returned value type</typeparam>
/// <param name="name">Query parameter name</param>
/// <returns>Query string value</returns>
public virtual T QueryString<T>(string name)
{
if (!IsRequestAvailable())
return default;
if (StringValues.IsNullOrEmpty(_httpContextAccessor.HttpContext.Request.Query[name]))
return default;
return CommonHelper.To<T>(_httpContextAccessor.HttpContext.Request.Query[name].ToString());
}
/// <summary>
/// Restart application domain
/// </summary>
public virtual void RestartAppDomain()
{
_hostApplicationLifetime.StopApplication();
}
/// <summary>
/// Gets a value that indicates whether the client is being redirected to a new location
/// </summary>
public virtual bool IsRequestBeingRedirected
{
get
{
var response = _httpContextAccessor.HttpContext.Response;
//ASP.NET 4 style - return response.IsRequestBeingRedirected;
int[] redirectionStatusCodes = [StatusCodes.Status301MovedPermanently, StatusCodes.Status302Found];
return redirectionStatusCodes.Contains(response.StatusCode);
}
}
/// <summary>
/// Gets or sets a value that indicates whether the client is being redirected to a new location using POST
/// </summary>
public virtual bool IsPostBeingDone
{
get
{
if (_httpContextAccessor.HttpContext.Items[NopHttpDefaults.IsPostBeingDoneRequestItem] == null)
return false;
return Convert.ToBoolean(_httpContextAccessor.HttpContext.Items[NopHttpDefaults.IsPostBeingDoneRequestItem]);
}
set => _httpContextAccessor.HttpContext.Items[NopHttpDefaults.IsPostBeingDoneRequestItem] = value;
}
/// <summary>
/// Gets current HTTP request protocol
/// </summary>
public virtual string GetCurrentRequestProtocol()
{
return IsCurrentConnectionSecured() ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
}
/// <summary>
/// Gets whether the specified HTTP request URI references the local host.
/// </summary>
/// <param name="req">HTTP request</param>
/// <returns>True, if HTTP request URI references to the local host</returns>
public virtual bool IsLocalRequest(HttpRequest req)
{
//source: https://stackoverflow.com/a/41242493/7860424
var connection = req.HttpContext.Connection;
if (IsIpAddressSet(connection.RemoteIpAddress))
{
//We have a remote address set up
return IsIpAddressSet(connection.LocalIpAddress)
//Is local is same as remote, then we are local
? connection.RemoteIpAddress.Equals(connection.LocalIpAddress)
//else we are remote if the remote IP address is not a loopback address
: IPAddress.IsLoopback(connection.RemoteIpAddress);
}
return true;
}
/// <summary>
/// Get the raw path and full query of request
/// </summary>
/// <param name="request">HTTP request</param>
/// <returns>Raw URL</returns>
public virtual string GetRawUrl(HttpRequest request)
{
//first try to get the raw target from request feature
//note: value has not been UrlDecoded
var rawUrl = request.HttpContext.Features.Get<IHttpRequestFeature>()?.RawTarget;
//or compose raw URL manually
if (string.IsNullOrEmpty(rawUrl))
rawUrl = $"{request.PathBase}{request.Path}{request.QueryString}";
return rawUrl;
}
/// <summary>
/// Gets whether the request is made with AJAX
/// </summary>
/// <param name="request">HTTP request</param>
/// <returns>Result</returns>
public virtual bool IsAjaxRequest(HttpRequest request)
{
ArgumentNullException.ThrowIfNull(request);
if (request.Headers == null)
return false;
return request.Headers.XRequestedWith == "XMLHttpRequest";
}
#endregion
}