第54章 图片URL的后端获取

news2024/11/16 20:48:47

1 注意:

    在.NetCore WebApi框架中,在默认情况下由于没有集成“UseStaticFiles”内置管道中间件方法,如果想要通过图片URL显示图片,由会显示“404”错误,必须先把“UseStaticFiles”内置管道中间件方法集成到.NetCore WebApi框架中,这样就会解决该错误。

2 集成“UseStaticFiles”内置管道中间件方法

1 Framework.Infrastructure.Extensions.ApplicationBuilderExtensions.UseNopStaticFiles

   /// <param name="application">.NetCore框架内置管道接口实例。</param>

        /// <summary>

        /// 【使用静态资源文件】

        /// <remarks>

        /// 摘要:

        ///     把静态资源文件内置管道中间件实例集成到.NetCore框架内置管道中,为当前程序访问当前程序中的静态资源文件提供内置管道方法支撑。

        /// </remarks>

        /// </summary>

        public static void UseNopStaticFiles(this IApplicationBuilder application)

        {

            application.UseStaticFiles();

        }

2 Framework.Infrastructure.StaticFilesStartup

using Core.Infrastructure;

using Framework.Infrastructure.Extensions;

using Microsoft.AspNetCore.Builder;

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

namespace Framework.Infrastructure

{

    /// <summary>

    /// 【静态资源文件启动--类】

    /// <remarks>

    /// 摘要:

    ///     通过该类中的方法成员,把静态资源文件内置管道中间件实例集成到.NetCore框架内置管道中,为当前程序访问当前程序中的静态资源文件提供内置管道方法支撑。

    /// </remarks>

    /// </summary>

    public class StaticFilesStartup : IStartup

    {

        #region 属性--接口实现

        /// <summary>

        /// 【顺序】

        /// <remarks>

        /// 摘要:

        ///     获取跨域限制启动类中管道中间件实例,被集成到内置管道实例中的顺序,默认值:200

        /// 注意:

        ///     .NetCore框架默定义了一些常用的内置管道中间件,并默认规定了这些内置管道中间件实例在内置管道实例中被调用的顺序,

        /// 如果不按照该默认顺序调用这些内置管道中间件,在程序执行时就会出现逻辑异常。

        /// </remarks>

        public int Order => 99; //静态资源文件内置管道中间件必须在路由内置管道中间件和自定义管道中间件之前之前被集成和调用,否则在程序执行时就会出现逻辑异常。

        #endregion

        #region 方法--接口实现

        /// <param name="services">.Net(Core)框架内置依赖注入容器实例。</param>

        /// <param name="configuration">.NetCore框架内置配置接口实例(存储着当前程序中所有*.json文件中的数据)</param>

        /// <summary>

        /// 【配置服务】

        /// <remarks>

        /// 摘要:

        ///     由于该类中所需要的实例,已经注入到内置容器中,所以该方法成员中没有依赖注入中间件/实例需要注入到内置依赖注入容器中。

        /// </remarks>

        /// </summary>

        public void ConfigureServices(IServiceCollection services, IConfiguration configuration)

        {

        }

        /// <param name="application">.NetCore框架内置管道接口实例。</param>

        /// <summary>

        /// 【配置】

        /// <remarks>

        /// 摘要:

        ///    该方法把静态资源文件内置管道中间件实例集成到.NetCore框架内置管道中。

        /// </remarks>

        /// </summary>

        public void Configure(IApplicationBuilder application)

        {

            application.UseNopStaticFiles();

        }

        #endregion

    }
}

 3 Core.IWebHelper

using Microsoft.AspNetCore.Http;

namespace Core

{

    /// <summary>

    /// Web助手--接口】

    /// <remarks>

    /// 摘要:

    ///     通过继承该接口具体实现类中的方法成员,获取客户端访问当前程序所使用的传输协议(HTTP/HTTPS)、客户端的IP地址、当前程序的根路由、指定页面路由及其相应查询键/值对字符串等。

    /// </remarks>

    public partial interface IWebHelper

    {

        #region 属性

        /// <summary>

        /// 【请求开始重定向?】

        /// <remarks>

        /// 摘要:

        ///     设置1个值false(开始重定向)/true(未开始重定向),该值指示客户端浏览器请求是否是1个需要被重定向的请求;

        /// 如果客户端浏览器所发送请求操作的状态码包含有“301/302”,则该请求就是1个需要被重定向的请求,为重定向跳转操作提供预先的数据支撑。

        /// </remarks>

        /// </summary>

        bool IsRequestBeingRedirected { get; }

        #endregion

        #region 方法

        /// <summary>

        /// 【获取引用URL

        /// <remarks>

        /// 摘要:

        ///     该方法通过客户端浏览器请求头字典属性成员实例,来获取1次指定请求中所包含的1个指定引用URL字符串。

        /// 引用URL的作用(https://blog.csdn.net/TanMengyi/article/details/109471246)

        ///     1、防盗链

        ///     2、防止恶意请求

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个指定引用URL字符串。

        /// </returns>

        /// </summary>

        string GetUrlReferrer();

        /// <summary>

        /// 【获取当前IP地址】

        /// <remarks>

        /// 摘要:

        ///     该方法通过客户端浏览器的1次指定请求,获取该客户端的IP地址字符串。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个指定客户端的IP地址字符串。

        /// </returns>

        /// </summary>

        string GetCurrentIpAddress();

        /// <param name="includeQueryString">指示是否为1个指定URL拼接1指定的查询字符串。</param>

        /// <param name="useSsl">指示对当前程序进行访问时是否使用HTTPS传输协议,默认值:null,即使用HTTPS传输协议。</param>

        /// <param name="lowercaseUrl">指示是否把指定URL中的字母全部转接为小写字母,默认值为:false,即不把指定页面的URL中的字母全部转接为小写字母。</param>

        /// <summary>

        /// 【获取指定页面URL

        /// <remarks>

        /// 摘要:

        ///    通过该方法获取1个指定的URL字符串,该URL字符串为当前程序中指定页面的渲染显示提供预先的数据支撑。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个指定的URL字符串。

        /// </returns>

        /// </summary>

        string GetThisPageUrl(bool includeQueryString, bool? useSsl = null, bool lowercaseUrl = false);

        /// <summary>

        /// 【当前连接安全?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(不安全)/true(安全),该值指示客户端浏览器请求对当前程序的请求操作是否使用的是HTTPS传输协议,如果是,则当前连接是安全的。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个值false(是静态资源)/true(不是静态资源)

        /// </returns>

        /// </summary>

        bool IsCurrentConnectionSecured();

        /// <param name="useSsl">指示对当前程序进行访问时是否使用HTTPS传输协议,默认值:true,即使用HTTPS传输协议。</param>

        /// <summary>

        /// 【获取当前程序根路由】

        /// <remarks>

        /// 摘要:

        ///     从客户端浏览器中获取对当前程序访问的根路由字符串(注意:根路由字符串的最后包含“/”字符)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     当前程序的的根路由字符串(注意:根路由字符串的最后包含“/”字符)

        /// </returns>

        /// </summary>

        string GetStoreHost(bool useSsl = true);

        /// <summary>

        /// 【静态资源?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(是静态资源)/true(不静态资源),该值指示客户端浏览器请求的是否是静态资源

        /// (一般情况下“wwwroot”文件夹中的持久化存储的文件都是静态资源;例如:https://localhost:7239/images/Avatar/Default.bmp,该请求就是对静态资源的1次请求)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个值false(是静态资源)/true(不是静态资源)

        /// </returns>

        /// </summary>

        bool IsStaticResource();

        /// <param name="url">1个指定的URL字符串。</param>

        /// <param name="key">1个指定的URL查询键/值字符串中的1个指定键。</param>

        /// <param name="values">数组实例,该实例存储着1个指定的URL查询键/值字符串中的1个指定键所对应的1/多个值(每个值之间用“,”分割)</param>

        /// <summary>

        /// 【修改查询字符串】

        /// <remarks>

        /// 摘要:

        ///     1个指定的URL查询键/值字符串中的1个指定键所对应的1/多个值进行修改更新操作(每个值之间用“,”分割)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     被修改更新了查询键/值字符串后的1个指定的绝对路径URL字符串(该绝对路径URL字符串即可能是本地的(当前程序的),可能是第3方服务平台中的)

        /// </returns>

        /// </summary>

        string ModifyQueryString(string url, string key, params string[] values);

        /// <param name="url">1个指定的URL字符串。</param>

        /// <param name="key">1个指定的URL查询键/值字符串中的1个指定键。</param>

        /// <param name="value">数该实例存储着1个指定的URL查询键/值字符串中的1个指定键所对应的1个值,默认值:null,即移除URL查询键/值字符串中的1个指定键/值对项。</param>

        /// <summary>

        /// 【移除查询字符串】

        /// <remarks>

        /// 摘要:

        ///     1个指定的URL查询键/值字符串中的1个指定键所对应的1/多个值进行修改更新操作(每个值之间用“,”分割)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     被修改更新了查询键/值字符串后的1个指定的绝对路径URL字符串(该绝对路径URL字符串即可能是本地的(当前程序的),可能是第3方服务平台中的)

        /// </returns>

        /// </summary>

        string RemoveQueryString(string url, string key, string value = null);

        /// <typeparam name="T">泛型类型实例(1个指定类的类型实例)</typeparam>

        /// <param name="name">URL查询键/值字符串中的1个指定键。</param>

        /// <summary>

        /// 【查询字符串】

        /// <remarks>

        /// 摘要:

        ///     该方法以泛型形式把URL查询键/值字符串中字符串类型的1个指定值转换为1个指定类型的1个指定实例。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个指定类型的1个指定实例。

        /// </returns>

        /// </summary>

        T QueryString<T>(string name);

        /// <summary>

        /// 【重置应用域】

        /// <remarks>

        /// 摘要:

        ///    该方法为客户端浏览器的自动重启操作提供方法支撑。

        /// 说明:

        ///     例如:在当前程序第1次数据库的初始化安装成功完成后,客户端浏览器就会通过该方法来自动关闭数据库的初始化安装页面,

        ///  从而使当程序重新加载所有的初始化数据,保证当前程序能够在客户端浏览器顺序的被运行。

        /// </remarks>

        /// </summary>

        void RestartAppDomain();

        /// <summary>

        /// 【获取当前请求协议】

        /// <remarks>

        /// 摘要:

        ///     从客户端浏览器中获取对当前程序进行请求访问时所使用的传输协议字符串:"http"/"https"

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     传输协议字符串:"http"/"https"

        /// </returns>

        /// </summary>

        string GetCurrentRequestProtocol();

        /// <param name="request">HTTP请求类的1个指定实例。</param>

        /// <summary>

        /// 【本地请求?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(通过代理)/true(未通过代理),该值指示客户端对当前程序的访问是否通过了代理软件或服务(例如:Nginx等,实际上前后端分离的中的跨域中间件实质上也是一种代理访问)

        /// 说明:

        ///     如果是本地请求“LocalIpAddress”“RemoteIpAddress”是相等的;如果是代理请求“LocalIpAddress”“RemoteIpAddress”是不等的。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个值false(通过代理)/true(未通过代理)

        /// </returns>

        /// </summary>

        bool IsLocalRequest(HttpRequest request);

        /// <param name="request">HTTP请求类的1个指定实例。</param>

        /// <summary>

        /// 【获取原URL

        /// <remarks>

        /// 摘要:

        ///     该方法主要为在执行登录操作成功后返回原来的指定页面,提供方法支撑。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    执行登录操作成功后返回原来的指定页面的URL字符串。

        /// </returns>

        /// </summary>

        string GetRawUrl(HttpRequest request);

        /// <param name="request">HTTP请求类的1个指定实例。</param>

        /// <summary>

        /// Ajax请求?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(不是通过Ajax脚笨命令进行触发的请求)/true(是通过Ajax脚笨命令进行触发的请求),该值指示客户端浏览器请求头字典属性成员实例中是否包含有Ajax请求值,如果有则客户端的请求是通过Ajax脚笨命令进行触发的请求。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个值false(不是通过Ajax脚笨命令进行触发的请求)/true(是通过Ajax脚笨命令进行触发的请求)

        /// </returns>

        /// </summary>

        bool IsAjaxRequest(HttpRequest request);

        #endregion

    }

}

4 Core.WebHelper

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;

namespace Core

{

    /// <summary>

    /// Web助手--类】

    /// <remarks>

    /// 摘要:

    ///     通过该类中的方法成员,获取客户端访问当前程序所使用的传输协议(HTTP/HTTPS)、客户端的IP地址、当前程序的根路由、指定页面路由及其相应查询键/值对字符串等。

    /// </remarks>

    public partial class WebHelper : IWebHelper

    {

        #region 拷贝构造方法与变量

        private readonly IActionContextAccessor _actionContextAccessor;

        private readonly IHostApplicationLifetime _hostApplicationLifetime;

        private readonly IHttpContextAccessor _httpContextAccessor;

        private readonly IUrlHelperFactory _urlHelperFactory;

        /// <summary>

        /// 【拷贝构建方法】

        /// <remarks>

        /// 摘要:

        ///     依赖注入容器通过拷贝构造方法,实例化该类中的变量成员。

        /// </remarks>

        /// </summary>

        public WebHelper(IActionContextAccessor actionContextAccessor,

            IHostApplicationLifetime hostApplicationLifetime,

            IHttpContextAccessor httpContextAccessor,

            IUrlHelperFactory urlHelperFactory)

        {

            _actionContextAccessor = actionContextAccessor;

            _hostApplicationLifetime = hostApplicationLifetime;

            _httpContextAccessor = httpContextAccessor;

            _urlHelperFactory = urlHelperFactory;

        }

        #endregion

        #region 属性

        /// <summary>

        /// 【请求开始重定向?】

        /// <remarks>

        /// 摘要:

        ///     设置1个值false(开始重定向)/true(未开始重定向),该值指示客户端浏览器请求是否是1个需要被重定向的请求;

        /// 如果客户端浏览器所发送请求操作的状态码包含有“301/302”,则该请求就是1个需要被重定向的请求,为重定向跳转操作提供预先的数据支撑。

        /// </remarks>

        /// </summary>

        public virtual bool IsRequestBeingRedirected

        {

            get

            {

                //获取1次的HTTP请求上下文的请求属性成员实例。

                var response = _httpContextAccessor.HttpContext.Response;

                //ASP.NET 4 style - return response.IsRequestBeingRedirected;

                //实例化1个包含有“301/302”状态码的数组实例。

                int[] redirectionStatusCodes = { StatusCodes.Status301MovedPermanently, StatusCodes.Status302Found };

                //如果数组实例中包含有1次的HTTP请求上下文的请求属性成员实例的状态码,则请求就是1个需要被重定向的请求。

                return redirectionStatusCodes.Contains(response.StatusCode);

            }

        }

        #endregion

        #region 方法--私有/保护

        /// <summary>

        /// 【有效请求?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(有效)/true(无效),该值指示是否可以通过客户端浏览器来获取HTTP上下文实例,或HTTP上下文的请求属性成员实例,来验证1个指定的请求操作是否有效。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个值false(失败)/true(成功)

        /// </returns>

        /// </summary>

        protected virtual bool IsRequestAvailable()

        {

            //如果客户端浏览器不能获取HTTP上下文实例,则该指定的请求操作是无有效的。

            if (_httpContextAccessor?.HttpContext == null)

                return false;

            try

            {

                //如果客户端浏览器不能获取HTTP上下文的请求属性成员实例,则该指定的请求操作是无有效的。

                if (_httpContextAccessor.HttpContext.Request == null)

                    return false;

            }

            catch (Exception)

            {

                return false;

            }

            return true;

        }

        /// <param name="address">1个指定的IP地址实例。</param>

        /// <summary>

        /// IP地址设置?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(IP地址设置不成立)/true(IP地址设置成立),该值指1个指定的IP地址实例是否不为空且不与IPv6规范实例相匹配,则IP地址设置成立。

        /// 说明:

        ///     要实现IPAddress.IPv6LoopbackIPv4的自动匹配,计算机设备和网络设备在软/硬件环境都必须支持IPAddress.IPv6Loopback

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个值false(IP地址设置不成立)/true(IP地址设置成立)

        /// </returns>

        /// </summary>

        protected virtual bool IsIpAddressSet(IPAddress address)

        {

            var rez =  address != null && address.ToString() != IPAddress.IPv6Loopback.ToString();

            return rez;

        }

        #endregion

        #region 方法--接口实现

        /// <summary>

        /// 【获取引用URL

        /// <remarks>

        /// 摘要:

        ///     该方法通过客户端浏览器请求头字典属性成员实例,来获取1次指定请求中所包含的1个指定引用URL字符串。

        /// 引用URL的作用(https://blog.csdn.net/TanMengyi/article/details/109471246)

        ///     1、防盗链

        ///     2、防止恶意请求

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个指定引用URL字符串。

        /// </returns>

        /// </summary>

        public virtual string GetUrlReferrer()

        {

            if (!IsRequestAvailable())

                return string.Empty;

            //通过客户端浏览器请求头字典属性成员实例,来获取1次指定请求中所包含的引用URL字符串。

            return _httpContextAccessor.HttpContext.Request.Headers[HeaderNames.Referer];

        }

        /// <summary>

        /// 【获取当前IP地址】

        /// <remarks>

        /// 摘要:

        ///     该方法通过客户端浏览器的1次指定请求,获取该客户端的IP地址字符串。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个指定客户端的IP地址字符串。

        /// </returns>

        /// </summary>

        public virtual string GetCurrentIpAddress()

        {

            if (!IsRequestAvailable())

                return string.Empty;

            //如果请求上下文的远程IP地址属性成员实例不与IP地址规范实例相匹配,则直接返回空字符串。

            if (_httpContextAccessor.HttpContext.Connection?.RemoteIpAddress is not IPAddress remoteIp)

                return "";

            //如果远程IP地址属性成员实例与IPv6。规范实例相匹配,则返回该客户端的IP地址字符串。

            //说明:

            //  1IPAddress.IPv6Loopback,包含IPv4

            //  2、要实现IPAddress.IPv6LoopbackIPv4的自动匹配,客户端的计算机设备和网络设备在软/硬件环境都必须支持IPAddress.IPv6Loopback

            if (remoteIp.Equals(IPAddress.IPv6Loopback))

                return IPAddress.Loopback.ToString();

            //如果客户端的计算机设备和网络设备在软/硬件方面不支持IPAddress.IPv6Loopback,则通过IPv4规范返回客户端的IP地址字符串。

            return remoteIp.MapToIPv4().ToString();

        }

        /// <param name="includeQueryString">指示是否为1个指定URL拼接1指定的查询字符串。</param>

        /// <param name="useSsl">指示对当前程序进行访问时是否使用HTTPS传输协议,默认值:null,即使用HTTPS传输协议。</param>

        /// <param name="lowercaseUrl">指示是否把指定URL中的字母全部转接为小写字母,默认值为:false,即不把指定页面的URL中的字母全部转接为小写字母。</param>

        /// <summary>

        /// 【获取指定页面URL

        /// <remarks>

        /// 摘要:

        ///    通过该方法获取1个指定的URL字符串,该URL字符串为当前程序中指定页面的渲染显示提供预先的数据支撑。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个指定的URL字符串。

        /// </returns>

        /// </summary>

        public virtual string GetThisPageUrl(bool includeQueryString, bool? useSsl = null, bool lowercaseUrl = false)

        {

            if (!IsRequestAvailable())

                return string.Empty;

            //获取根路由字符串。

            var storeLocation = GetStoreHost(useSsl ?? IsCurrentConnectionSecured());

            //通过根路由字符串与指定控制器行为方法的字符串拼接出1个指定页面的URL字符串。

            var pageUrl = $"{storeLocation.TrimEnd('/')}{_httpContextAccessor.HttpContext.Request.Path}";

            //1个指定页面的URL字符串添加拼接查询字符。

            if (includeQueryString)

                pageUrl = $"{pageUrl}{_httpContextAccessor.HttpContext.Request.QueryString}";

            //1个指定页面的URL字符串中的字母全部转换为小写。

            if (lowercaseUrl)

                pageUrl = pageUrl.ToLowerInvariant();

            return pageUrl;

        }

        /// <summary>

        /// 【当前连接安全?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(不安全)/true(安全),该值指示客户端浏览器请求对当前程序的请求操作是否使用的是HTTPS传输协议,如果是,则当前连接是安全的。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个值false(是静态资源)/true(不是静态资源)

        /// </returns>

        /// </summary>

        public virtual bool IsCurrentConnectionSecured()

        {

            if (!IsRequestAvailable())

                return false;

            return _httpContextAccessor.HttpContext.Request.IsHttps;

        }

        /// <param name="useSsl">指示对当前程序进行访问时是否使用HTTPS传输协议,默认值:true,即使用HTTPS传输协议。</param>

        /// <summary>

        /// 【获取当前程序根路由】

        /// <remarks>

        /// 摘要:

        ///     从客户端浏览器中获取对当前程序访问的根路由字符串(注意:根路由字符串的最后包含“/”字符)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     当前程序的的根路由字符串(注意:根路由字符串的最后包含“/”字符)

        /// </returns>

        /// </summary>

        public virtual string GetStoreHost(bool useSsl = true)

        {

            if (!IsRequestAvailable())

                return string.Empty;

            //客户端浏览器中获取对当前程序访问的根路由。

            var hostHeader = _httpContextAccessor.HttpContext.Request.Headers[HeaderNames.Host];

            if (StringValues.IsNullOrEmpty(hostHeader))

                return string.Empty;

            //把传输协议与根路由结合拼接出根路由字符串。

            //Uri.UriSchemeHttps:"https"

            //Uri.UriSchemeHttp:"http"

            //Uri.SchemeDelimiter:"://"

            var storeHost = $"{(useSsl ? Uri.UriSchemeHttps : Uri.UriSchemeHttp)}{Uri.SchemeDelimiter}{hostHeader.FirstOrDefault()}";

            //确保根路由字符串的最后包含“/”字符。

            storeHost = $"{storeHost.TrimEnd('/')}/";

            return storeHost;

        }

        /// <summary>

        /// 【静态资源?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(是静态资源)/true(不静态资源),该值指示客户端浏览器请求的是否是静态资源

        /// (一般情况下“wwwroot”文件夹中的持久化存储的文件都是静态资源;例如:https://localhost:7239/images/Avatar/Default.bmp,该请求就是对静态资源的1次请求)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    1个值false(是静态资源)/true(不是静态资源)

        /// </returns>

        /// </summary>

        public virtual bool IsStaticResource()

        {

            if (!IsRequestAvailable())

                return false;

            string path = _httpContextAccessor.HttpContext.Request.Path;

            //通过FileExtensionContentTypeProvider实例,来判断一个指定请求的路径字符串所对应的服务器端中的文件,是否为静态资源文件。

            //FileExtensionContentTypeProvider实例定义了,大量常用静态资源文件的扩展名,该实例通过扩展名来检索一个文件,是否为静态资源文件。

            //原代码见: https://github.com/aspnet/StaticFiles/tree/master/src/Microsoft.AspNetCore.StaticFiles/FileExtensionContentTypeProvider.cs

            var contentTypeProvider = new FileExtensionContentTypeProvider();

            return contentTypeProvider.TryGetContentType(path, out var _);

        }

        /// <param name="url">1个指定的URL字符串。</param>

        /// <param name="key">1个指定的URL查询键/值字符串中的1个指定键。</param>

        /// <param name="values">数组实例,该实例存储着1个指定的URL查询键/值字符串中的1个指定键所对应的1/多个值(每个值之间用“,”分割)</param>

        /// <summary>

        /// 【修改查询字符串】

        /// <remarks>

        /// 摘要:

        ///     1个指定的URL查询键/值字符串中的1个指定键所对应的1/多个值进行修改更新操作(每个值之间用“,”分割)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     被修改更新了查询键/值字符串后的1个指定的绝对路径URL字符串(该绝对路径URL字符串即可能是本地的(当前程序的),可能是第3方服务平台中的)

        /// </returns>

        /// </summary>

        public virtual string ModifyQueryString(string url, string key, params string[] values)

        {

            if (string.IsNullOrEmpty(url))

                return string.Empty;

            if (string.IsNullOrEmpty(key))

                return url;

            //通过当前程序中的指定控制器的指定行为方法,来验证1个指定的URL字符串,是否是相对路径URL字符串,也可以这样理解相对路径URL字符串一定是本地的(当前程序的),绝对路径URL字符串可能是本地的也可能是第3方服务平台中的。

            var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);

            var isLocalUrl = urlHelper.IsLocalUrl(url);

            //如果是相对路径URL字符串,则拼接出相应的绝对路径URL字符串。

            var uriStr = url;

            if (isLocalUrl)

            {

                var pathBase = _httpContextAccessor.HttpContext.Request.PathBase;

                uriStr = $"{GetStoreHost().TrimEnd('/')}{(url.StartsWith(pathBase) ? url.Replace(pathBase, "") : url)}";

            }

            //根据拼接出相应的绝对路径URL字符串,实例化1个指定的URI实例。

            var uri = new Uri(uriStr, UriKind.Absolute);

            //URL查询键/值字符串,实列化存储到字典实例中。

            var queryParameters = QueryHelpers.ParseQuery(uri.Query);

            //1/多个值(每个值之间用“,”分割)存储到字典实例的指定键/值对项中。

            queryParameters[key] = string.Join(",", values);

            //通过的字典实例,实例化查询生成器实例。

            //parameter.Value.FirstOrDefault()?.ToString():如果1个指定键所对应的多个值, 在极端情况下这些值中有相同的,那么只获取第1(在实际开发中这种场景是不可能出现的)

            var queryBuilder = new QueryBuilder(queryParameters

                .ToDictionary(parameter => parameter.Key, parameter => parameter.Value.FirstOrDefault()?.ToString() ?? string.Empty));

            //通过查询生成器实例,获取修改更新了查询键/值字符串绝对路径URL字符串(该绝对路径URL字符串即可能是本地的(当前程序的),可能是第3方服务平台中的)

            url = $"{(isLocalUrl ? uri.LocalPath : uri.GetLeftPart(UriPartial.Path))}{queryBuilder.ToQueryString()}{uri.Fragment}";

            return url;

        }

        /// <param name="url">1个指定的URL字符串。</param>

        /// <param name="key">1个指定的URL查询键/值字符串中的1个指定键。</param>

        /// <param name="value">数该实例存储着1个指定的URL查询键/值字符串中的1个指定键所对应的1个值,默认值:null,即移除URL查询键/值字符串中的1个指定键/值对项。</param>

        /// <summary>

        /// 【移除查询字符串】

        /// <remarks>

        /// 摘要:

        ///     1个指定的URL查询键/值字符串中的1个指定键所对应的1/多个值进行修改更新操作(每个值之间用“,”分割)

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     被修改更新了查询键/值字符串后的1个指定的绝对路径URL字符串(该绝对路径URL字符串即可能是本地的(当前程序的),可能是第3方服务平台中的)

        /// </returns>

        /// </summary>

        public virtual string RemoveQueryString(string url, string key, string value = null)

        {

            if (string.IsNullOrEmpty(url))

                return string.Empty;

            if (string.IsNullOrEmpty(key))

                return url;

            //通过当前程序中的指定控制器的指定行为方法,来验证1个指定的URL字符串,是否是相对路径URL字符串,也可以这样理解相对路径URL字符串一定是本地的(当前程序的),绝对路径URL字符串可能是本地的也可能是第3方服务平台中的。

            var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);

            var isLocalUrl = urlHelper.IsLocalUrl(url);

            //如果是相对路径URL字符串,则拼接出相应的绝对路径URL字符串;或直接获取绝对路径URL字符串。

            var uri = new Uri(isLocalUrl ? $"{GetStoreHost().TrimEnd('/')}{url}" : url, UriKind.Absolute);

            //URL查询键/值字符串,实列化存储到字典实例中。

            var queryParameters = QueryHelpers.ParseQuery(uri.Query)

                .SelectMany(parameter => parameter.Value, (parameter, queryValue) => new KeyValuePair<string, string>(parameter.Key, queryValue))

                .ToList();

            //如果存在被移除的值,则从字典实例的指定键/值对项中移除该值。

            if (!string.IsNullOrEmpty(value))

            {

                queryParameters.RemoveAll(parameter => parameter.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)

                    && parameter.Value.Equals(value, StringComparison.InvariantCultureIgnoreCase));

            }

            else

            {

                //如果不存在被移除的值,则从字典实例中移除整个指定键/值对项。

                queryParameters.RemoveAll(parameter => parameter.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));

            }

            var queryBuilder = new QueryBuilder(queryParameters);

            //拼接出被移除了指定查询键/值对项后的的1个指定的绝对路径URL字符串(该绝对路径URL字符串即可能是本地的(当前程序的),可能是第3方服务平台中的)

            url = $"{(isLocalUrl ? uri.LocalPath : uri.GetLeftPart(UriPartial.Path))}{queryBuilder.ToQueryString()}{uri.Fragment}";

            return url;

        }

        /// <typeparam name="T">泛型类型实例(1个指定类的类型实例)</typeparam>

        /// <param name="name">URL查询键/值字符串中的1个指定键。</param>

        /// <summary>

        /// 【查询字符串】

        /// <remarks>

        /// 摘要:

        ///     该方法以泛型形式把URL查询键/值字符串中字符串类型的1个指定值转换为1个指定类型的1个指定实例。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个指定类型的1个指定实例。

        /// </returns>

        /// </summary>

        public virtual T QueryString<T>(string name)

        {

            if (!IsRequestAvailable())

                return default;

            //如果URL查询键/值字符串中的1个指定值为空,则根据指定类型实例转换为相应的默认实例,例如:int类型的默认实例为:0

            if (StringValues.IsNullOrEmpty(_httpContextAccessor.HttpContext.Request.Query[name]))

                return default;

            //URL查询键/值字符串中字符串类型的1个指定值转换为1个指定类型的1个指定实例。

            return CommonHelper.To<T>(_httpContextAccessor.HttpContext.Request.Query[name].ToString());

        }

        /// <summary>

        /// 【重置应用域】

        /// <remarks>

        /// 摘要:

        ///    该方法为客户端浏览器的自动重启操作提供方法支撑。

        /// 说明:

        ///     例如:在当前程序第1次数据库的初始化安装成功完成后,客户端浏览器就会通过该方法来自动关闭数据库的初始化安装页面,

        ///  从而使当程序重新加载所有的初始化数据,保证当前程序能够在客户端浏览器顺序的被运行。

        /// </remarks>

        /// </summary>

        public virtual void RestartAppDomain()

        {

            _hostApplicationLifetime.StopApplication();

        }

        /// <summary>

        /// 【获取当前请求协议】

        /// <remarks>

        /// 摘要:

        ///     从客户端浏览器中获取对当前程序进行请求访问时所使用的传输协议字符串:"http"/"https"

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     传输协议字符串:"http"/"https"

        /// </returns>

        /// </summary>

        public virtual string GetCurrentRequestProtocol()

        {

            return IsCurrentConnectionSecured() ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;

        }

        /// <param name="request">HTTP请求类的1个指定实例。</param>

        /// <summary>

        /// 【本地请求?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(通过代理)/true(未通过代理),该值指示客户端对当前程序的访问是否通过了代理软件或服务(例如:Nginx等,实际上前后端分离的中的跨域中间件实质上也是一种代理访问)

        /// 说明:

        ///     如果是本地请求“LocalIpAddress”“RemoteIpAddress”是相等的;如果是代理请求“LocalIpAddress”“RemoteIpAddress”是不等的。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个值false(通过代理)/true(未通过代理)

        /// </returns>

        /// </summary>

        public virtual bool IsLocalRequest(HttpRequest request)

        {

            //source: https://stackoverflow.com/a/41242493/7860424

            var connection = request.HttpContext.Connection;

            //如果:远程IP地址实例不符合IPV6规范。

            if (IsIpAddressSet(connection.RemoteIpAddress))

            {

                //如果:本地IP地址实例不符合IPV6规范。

                return IsIpAddressSet(connection.LocalIpAddress)

                    //如果“LocalIpAddress”“RemoteIpAddress”是相等,则是本地请求,即未通过代理;反之则是代理请求。

                    ? connection.RemoteIpAddress.Equals(connection.LocalIpAddress)

                    //如果:本地IP地址实例符合IPV6规范,“RemoteIpAddress”符合IPV6本地规范,则是本地请求,即未通过代理;反之则是代理请求。

                    : IPAddress.IsLoopback(connection.RemoteIpAddress);

            }

            //IPV6规范实质都是代理请求?

            //由于IPV6规范在设计之初都不兼容IPV4规范,为了使IPV6规范兼容IPV4规范,IPV6规范实质都是代理请求(来源于网络不知是否正确)

            return true;

        }

        /// <param name="request">HTTP请求类的1个指定实例。</param>

        /// <summary>

        /// 【获取原URL

        /// <remarks>

        /// 摘要:

        ///     该方法主要为在执行登录操作成功后返回原来的指定页面,提供方法支撑。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    执行登录操作成功后返回原来的指定页面的URL字符串。

        /// </returns>

        /// </summary>

        public virtual string GetRawUrl(HttpRequest request)

        {

            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;

        }

        /// <param name="request">HTTP请求类的1个指定实例。</param>

        /// <summary>

        /// Ajax请求?】

        /// <remarks>

        /// 摘要:

        ///     获取1个值false(不是通过Ajax脚笨命令进行触发的请求)/true(是通过Ajax脚笨命令进行触发的请求),该值指示客户端浏览器请求头字典属性成员实例中是否包含有Ajax请求值,如果有则客户端的请求是通过Ajax脚笨命令进行触发的请求。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个值false(不是通过Ajax脚笨命令进行触发的请求)/true(是通过Ajax脚笨命令进行触发的请求)

        /// </returns>

        /// </summary>

        public virtual bool IsAjaxRequest(HttpRequest request)

        {

            if (request == null)

                throw new ArgumentNullException(nameof(request));

            if (request.Headers == null)

                return false;

            return request.Headers["X-Requested-With"] == "XMLHttpRequest";

        }

        #endregion

    }

}

5 重构Framework.Infrastructure.DependencyInjectionStartup. ConfigureServices

     services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

     services.AddScoped<INopFileProvider, NopFileProvider>();

     services.AddScoped<IWebHelper, WebHelper>();

6 WebApi.Controllers.CustomerController. GetAvatarUrl

/// <param name="customerId">1个指定的长整型值。</param>

        /// <summary>

        /// 【头像图片绝对URL--无需权限】

        /// </summary>

        /// <remarks>

        /// 摘要:

        ///     获取1个指定用户头像图片的绝对URL字符串,为前端头像图片的渲染显示提供数据支撑。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个指定用户头像图片的绝对URL字符串,为前端头像图片的渲染显示提供数据支撑。

        /// </returns>

        [HttpGet]

        public async Task<MessageModel<string>> GetAvatarUrl(long customerId)

        {

            string _avatarUrl = string.Empty;

            Customer _customer = await _customerService.GetCustomerByIdAsync(customerId);

            if (_customer!=null && string.IsNullOrEmpty(_customer.Avatar))

            {

                string _path = _nopFileProvider.Combine(_nopFileProvider.WebRootPath, @"\images\Avatar\Default.jpg");

                if (_nopFileProvider.FileExists(_path))

                {

                    string _absoluteAvatarUrl = _nopFileProvider.GetVirtualPath(@"\images\Avatar\Default.jpg");

                    //URL格式路径字符中的第一个字符:“~/”

                    _absoluteAvatarUrl = _absoluteAvatarUrl.Replace("~/", string.Empty);

                    _avatarUrl = _webHelper.GetStoreHost() + _absoluteAvatarUrl;

                }

            }

           

            if(_customer != null && !string.IsNullOrEmpty(_customer.Avatar))

            {

                _avatarUrl = _webHelper.GetStoreHost().TrimEnd('/') + _customer.Avatar;

            }

            if(_customer == null || string.IsNullOrEmpty(_avatarUrl))

                return MessageModel<string>.Fail("获取指定用户头像图片的绝对URL字符串失败!", 500);

            return MessageModel<string>.GetSuccess("成功获取指定用户头像图片的绝对URL字符串!", _avatarUrl);

        }

    对以上功能更为具体实现和注释见:230309_044shopDemo(图片URL的后端获取)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/399055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安装MySQL数据库8.0服务实例

前言 之前尝试去安装了MySQL5.7的社区版本&#xff0c;今天来安装MySQL8.0的版本&#xff0c;并且以两种方式进行安装&#xff0c;一个是通过RPM包的安装&#xff0c;另一个则是编译的方式。 一. 前期准备 查看服务器IP [rootlocalhost ~]# hostname -I 192.168.161.166 19…

js中window自带的四舍五入toFixed方法中的坑以及解决办法

Hello&#xff0c;各位&#xff0c;我胡汉三~啊呸&#xff0c;我又回来啦&#xff0c;还改了名&#xff0c;换了头像&#xff0c;哈哈哈&#xff01;时隔这么长时间不更新了&#xff0c;太忙了&#xff0c;平时笔记都记在了自己的电脑上&#xff0c;从今天起&#xff0c;继续更…

vxe-grid 全局自定义filter过滤器,支持字典过滤

一、vxe-table的全局筛选器filters的实现 官网例子&#xff1a;https://vxetable.cn/#/table/renderer/filter 进入之后&#xff1a;我们可以参照例子自行实现&#xff0c;也可以下载它的源码&#xff0c;进行调整 下载好后并解压&#xff0c;用vscode将解压后的文件打开。全局…

CRM系统中的营销自动化能解决什么问题

CRM客户管理系统营销自动化的范围远远超出了人们的认知。许多人认为它只是自动化完成重复和乏味的任务来减少营销人员的工作量。虽然这确实占了很大一部分&#xff0c;但它真正的价值在于提高潜客转化&#xff0c;增加业务收入。那么&#xff0c;什么是CRM系统营销自动化&#…

195、【动态规划】AcWing —— 91. 最短Hamilton路径(C++版本)

题目描述 原题链接&#xff1a;91. 最短Hamilton路径 解题思路 动态规划五步曲&#xff1a; &#xff08;1&#xff09;dp[i][j]含义&#xff1a; 到达点j并且状态为i时&#xff0c;具有的最短路径长度&#xff0c;其中状态j用状态压缩二进制的方式表示。j中从0-n-1位分别对…

【玩转c++】List讲解和模拟底层实现

本期主题&#xff1a;list的讲解和模拟实现博客主页&#xff1a;小峰同学分享小编的在Linux中学习到的知识和遇到的问题小编的能力有限&#xff0c;出现错误希望大家不吝赐1.list的介绍和使用1.1.list的介绍1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器&…

Good Idea, 利用MySQL JSON特性优化千万级文库表

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 利用MySQL JSON特性优化千万级文库表 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f…

常见问题整理1

目录 偏差和方差 欠拟合underfitting 过拟合overfitting 梯度消失和梯度爆炸 归一化 偏差和方差 偏差&#xff1a;算法期望预测和真实预测之间的偏差程度。反应的是模型本身的拟合能力。 方差&#xff1a;度量了同等大小的训练集的变动导致学习性能的变化&#xff0c;刻画…

万字长文带你走进MySql优化(系统层面优化、软件层面优化、SQL层面优化)

文章目录系统层面优化采用分布式架构使用缓存使用搜索引擎软件层面优化调整 MySQL 参数配置定期清理无用数据创建索引创建索引普通索引唯一索引全文索引组合索引空间索引主键索引外键索引索引前缀适合创建索引的场景不适合创建索引的场景优化表结构分库分表SQL优化explain执行计…

Python3-File(文件) 方法

Python3 File(文件) 方法 open() 方法 Python open() 方法用于打开一个文件&#xff0c;并返回文件对象。 在对文件进行处理过程都需要使用到这个函数&#xff0c;如果该文件无法被打开&#xff0c;会抛出 OSError。 注意&#xff1a;使用 open() 方法一定要保证关闭文件对…

Nowcoder .链表分割

文章目录哨兵位节点哨兵位节点 链表分割 小于X 尾插到一个新链表 大于等于X 尾插到另一个链表 最后将两个链表链接起来 需要注意的细节&#xff1a;将第一个链表的尾与第二个链表的头相连接&#xff0c;再返回连接后的整个链表的头&#xff08;哨兵位头节点的下一个&#xff0…

ECharts 环形图组件封装

一、ECharts引入1.安装echarts包npm install echarts --save2.引入echarts这里就演示全局引入了&#xff0c;挂载到vue全局&#xff0c;后面使用时&#xff0c;直接使用 $echartsimport * as echarts from echarts Vue.prototype.$echarts echarts二、写echarts组件这里演示环…

【蓝桥杯嵌入式】PWM的设置,原理图解析与代码实现(第十一届省赛为例)——STM32

&#x1f38a;【蓝桥杯嵌入式】专题正在持续更新中&#xff0c;原理图解析✨&#xff0c;各模块分析✨以及历年真题讲解✨都在这儿哦&#xff0c;欢迎大家前往订阅本专题&#xff0c;获取更多详细信息哦&#x1f38f;&#x1f38f;&#x1f38f; &#x1fa94;本系列专栏 - 蓝…

Lombok 常用注解

文章目录简介MAVEN 依赖常用的注解1. Data 注解 :2. Setter 注解&#xff1a;3.Getter 注解&#xff1a;4.Log4j or Slf4j 注解5.NoArgsConstructor注解&#xff1a;6.AllArgsConstructor注解&#xff1a;7.RequiredArgsConstructor注解:8.Builder注解&#xff1a;9.Cleanup注解…

全国青少年软件编程(Scratch)等级考试一级真题——2019.12

青少年软件编程&#xff08;Scratch&#xff09;等级考试试卷&#xff08;一级&#xff09;分数&#xff1a;100 题数&#xff1a;37一、单选题(共25题&#xff0c;每题2分&#xff0c;共50分)1.下列关于舞台的描述&#xff0c;不正确的是&#xff1f;&#xff08; &#xff09…

DMHS搭建DMDSC 2节点集群同步到单库

DMHS搭建DMDSC 2节点集群同步到单库环境介绍1 安装DMOCI1.1 关闭数据库实例服务1.2 将DMOCI 复制到源端与目的端的数据库bin目录1.3 对数据库bin 执行目录文件更改用户属组和权限2 启动源数据库服务并配置数据库实例参数2.1 使用DMCSSM启动集群实例2.2 DMDSC源其中一个节点执行…

为何英格兰与苏格兰同属英国,却争端不断?

英国作为世界上的强国其组成部分是由大不列颠岛上的英格兰、威尔士和苏格兰以及爱尔兰岛东北部的北爱尔兰以及一系列附属岛屿。英国作为西欧的一个岛国&#xff0c;地理位置十分优越&#xff0c;位于欧洲大陆西北面的不列颠群岛。英国作为西欧的一颗不容忽视的“明星”&#xf…

【Unity风格化草地】概述风格化草地的实现方法

写在前面 最近本专业开始多很多事情了&#xff0c;要开始建模写论文了&#xff08;不然研究生毕不了业&#xff09;&#xff0c;TA方面的学习进度更慢了&#xff0c;&#xff0c;so sad。 废话不多说&#xff0c;这篇文章其实是个小总结&#xff0c;毕竟学习新东西就是先要当…

序列索引序列切片

六个标准数据类型中是序列的有&#xff1a;字符串&#xff08;String&#xff09;、列表&#xff08;List&#xff09;、元组&#xff08;Tuple&#xff09;。 通过索引和切片的方式可以访问序列中的元素。 一、序列索引 序列中的每一个元素都有一个属于自己的编号&#xff0c…

演唱会的火车票没了?Python实现12306查票以及zidong购票....

嗨害大家好&#xff01;我是小熊猫~ 不知道大家抢到演唱会的门票没有呢? 不管抢到没有&#xff0c;火车票也是很重要的哇 24小时抢票不间断的那种喔~ ~ ~ 不然可就要走路去了喔~ 准备工作 环境 Python 3.8Pycharm 插件 谷歌浏览器驱动 模块 需要安装的第三方模块&am…