ASP.NET Core 2.0 自定义 _ViewStart 和 _ViewImports 的目录位置

在 ASP.NET Core 里扩展 Razor 查找视图目录不是什么新鲜和困难的事情,但 _ViewStart 和 _ViewImports 这2个视图比较特殊,如果想让 Razor 在我们指定的目录中查找它们,则需要耗费一点额外的精力。本文将提供一种方法做到这一点。注意,文本仅适用于 ASP.NET Core 2.0+, 因为 Razor 在 2.0 版本里的内部实现有较大重构,因此这里提供的方法并不适用于 ASP.NET Core 1.x

为了全面描述 ASP.NET Core 2.0 中扩展 Razor 查找视图目录的能力,我们还是由浅入深,从最简单的扩展方式着手吧。

准备工作

首先,我们可以创建一个新的 ASP.NET Core 项目用于演示。

mkdir CustomizedViewLocation
cd CustomizedViewLocationdotnet new web # 创建一个空的 ASP.NET Core 应用

接下来稍微调整下 Startup.cs 文件的内容,引入 MVC:

// Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace CustomizedViewLocation{  

public class Startup{      
     public void ConfigureServices(IServiceCollection services)        {services.AddMvc();}      
     
     public void Configure(IApplicationBuilder app, IHostingEnvironment env)        {app.UseMvcWithDefaultRoute();}} }

好了我们的演示项目已经搭好了架子。

我们的目标

在我们的示例项目中,我们希望我们的目录组织方式是按照功能模块组织的,即同一个功能模块的所有 Controller 和 View 都放在同一个目录下。对于多个功能模块共享、通用的内容,比如 _Layout_Footer_ViewStart 和 _ViewImports 则单独放在根目录下的一个叫 Shared 的子目录中。

最简单的方式: ViewLocationFormats

假设我们现在有2个功能模块 Home 和 About,分别需要 HomeController 和它的 Index view,以及 AboutMeController 和它的 Index view. 因为一个 Controller 可能会包含多个 view,因此我选择为每一个功能模块目录下再增加一个 Views 目录,集中这个功能模块下的所有 View. 整个目录结构看起来是这样的:

从目录结构中我们可以发现我们的视图目录为 /{controller}/Views/{viewName}.cshtml, 比如 HomeController 的 Index 视图所在的位置就是 /Home/Views/Index.cshtml,这跟 MVC 默认的视图位置 /Views/{Controller}/{viewName}.cshtml 很相似(/Views/Home/Index.cshtml),共同的特点是路径中的 Controller 部分和 View 部分是动态的,其它的都是固定不变的。其实 MVC 默认的寻找视图位置的方式一点都不高端,类似于这样:

string controllerName = "Home"; // “我”知道当前 Controller 是 Home
string viewName = "Index"; // "我“知道当前需要解析的 View 的名字// 把 viewName 和 controllerName 带入一个代表视图路径的格式化字符串得到最终的视图路径。
string viewPath = string.Format("/Views/{1}/{0}.cshtml", viewName, controllerName);// 根据 viewPath 找到视图文件做后续处理

如果我们可以构建另一个格式字符串,其中 {0} 代表 View 名称, {1} 代表 Controller 名称,然后替换掉默认的 /Views/{1}/{0}.cshtml,那我们就可以让 Razor 到我们设定的路径去检索视图。而要做到这点非常容易,利用 ViewLocationFormats,代码如下:

// Startup.cs
public void ConfigureServices(IServiceCollection services){IMvcBuilder mvcBuilder = services.AddMvc();mvcBuilder.AddRazorOptions(options => options.ViewLocationFormats.Add("/{1}/Views/{0}.cshtml")); }

收工,就这么简单。顺便说一句,还有一个参数 {2},代表 Area 名称。

这种做法是不是已经很完美了呢?No, No, No. 谁能看出来这种做法有什么缺点?

这种做法有2个缺点。

  1. 所有的功能模块目录必须在根目录下创建,无法建立层级目录关系。且看下面的目录结构截图:

注意 Reports 目录,因为我们有种类繁多的报表,因此我们希望可以把各种报表分门别类放入各自的目录。但是这么做之后,我们之前设置的 ViewLocationFormats 就无效了。例如我们访问 URL /EmployeeReport/Index, Razor 会试图寻找 /EmployeeReport/Views/Index.cshtml,但其真正的位置是 /Reports/AdHocReports/EmployeeReport/Views/Index.cshtml。前面还有好几层目录呢~

  1. 因为所有的 View 文件不再位于同一个父级目录之下,因此 _ViewStart.cshtml 和 _ViewImports.cshtml 的作用将受到极大限制。原因后面细表。

下面我们来分别解决这2个问题。

最灵活的方式: IViewLocationExpander

有时候,我们的视图目录除了 controller 名称 和 view 名称2个变量外,还涉及到别的动态部分,比如上面的 Reports 相关 Controller,视图路径有更深的目录结构,而 controller 名称仅代表末级的目录。此时,我们需要一种更灵活的方式来处理: IViewLocationExpander,通过实现 IViewLocationExpander,我们可以得到一个 ViewLocationExpanderContext,然后据此更灵活地创建 view location formats。

对于我们要解决的目录层次问题,我们首先需要观察,然后会发现目录层次结构和 Controller 类型的命名空间是有对应关系的。例如如下定义:

using Microsoft.AspNetCore.Mvc;

namespace CustomizedViewLocation.Reports.AdHocReports.EmployeeReport{  

 public class EmployeeReportController : Controller{        public IActionResult Index() => View();} }

观察 EmployeeReportController 的命名空间 CustomizedViewLocation.Reports.AdHocReports.EmployeeReport以及 Index 视图对应的目录 /Reports/AdHocReports/EmployeeReport/Views/Index.cshtml 可以发现如下对应关系:

命名空间视图路径ViewLocationFormat
CustomizedViewLocation项目根路径/
Reports.AdHocReportsReports/AdHocReports把整个命名空间以“.”为分割点掐头去尾,然后把“.”替换为“/”
EmployeeReportEmployeeReportController 名称

Views固定目录

Index.cshtml视图名称.cshtml

所以我们 IViewLocationExpander 的实现类型主要是获取和处理 Controller 的命名空间。且看下面的代码。

// NamespaceViewLocationExpander.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;

namespace CustomizedViewLocation{  

 public class NamespaceViewLocationExpander : IViewLocationExpander{      
  private const string VIEWS_FOLDER_NAME = "Views";    
     public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)        {ControllerActionDescriptor cad = context.ActionContext.ActionDescriptor as ControllerActionDescriptor;            string controllerNamespace = cad.ControllerTypeInfo.Namespace;            int firstDotIndex = controllerNamespace.IndexOf('.');            int lastDotIndex = controllerNamespace.LastIndexOf('.');            if (firstDotIndex < 0)          
           return viewLocations;          
            string viewLocation;          
            if (firstDotIndex == lastDotIndex){                // controller folder is the first level sub folder of root folderviewLocation = "/{1}/Views/{0}.cshtml";}          
             else{              
               string viewPath = controllerNamespace.Substring(firstDotIndex + 1, lastDotIndex - firstDotIndex - 1).Replace(".", "/");viewLocation = $"/{viewPath}/{{1}}/Views/{{0}}.cshtml";}        
            if (viewLocations.Any(l => l.Equals(viewLocation, StringComparison.InvariantCultureIgnoreCase)))            
             return viewLocations;          
             if (viewLocations is List<string> locations){locations.Add(viewLocation);      
                      return locations;}            // it turns out the viewLocations from ASP.NET Core is List<string>, so the code path should not go here.List<string> newViewLocations = viewLocations.ToList();newViewLocations.Add(viewLocation);      
                            return newViewLocations;}  
         
              public void PopulateValues(ViewLocationExpanderContext context)        {}} }

上面对命名空间的处理略显繁琐。其实你可以不用管,重点是我们可以得到 ViewLocationExpanderContext,并据此构建新的 view location format 然后与现有的 viewLocations 合并并返回给 ASP.NET Core。

细心的同学可能还注意到一个空的方法 PopulateValues,这玩意儿有什么用?具体作用可以参照这个 StackOverflow 的问题,基本上来说,一旦某个 Controller 及其某个 View 找到视图位置之后,这个对应关系就会缓存下来,以后就不会再调用 ExpandViewLocations方法了。但是,如果你有这种情况,就是同一个 Controller, 同一个视图名称但是还应该依据某些特别条件去找不同的视图位置,那么就可以利用 PopulateValues 方法填充一些特定的 Value, 这些 Value 会参与到缓存键的创建, 从而控制到视图位置缓存的创建。

下一步,把我们的 NamespaceViewLocationExpander 注册一下:

// Startup.cs
public void ConfigureServices(IServiceCollection services){IMvcBuilder mvcBuilder = services.AddMvc();mvcBuilder.AddRazorOptions(options => {        // options.ViewLocationFormats.Add("/{1}/Views/{0}.cshtml"); we don't need this any more if we make use of NamespaceViewLocationExpanderoptions.ViewLocationExpanders.Add(new NamespaceViewLocationExpander());}); }

另外,有了 NamespaceViewLocationExpander, 我们就不需要前面对 ViewLocationFormats 的追加了,因为那种情况作为一种特例已经在 NamespaceViewLocationExpander 中处理了。
至此,目录分层的问题解决了。

_ViewStart.cshtml 和 _ViewImports 的起效机制与调整

对这2个特别的视图,我们并不陌生,通常在 _ViewStart.cshtml 里面设置 Layout 视图,然后每个视图就自动地启用了那个 Layout 视图,在 _ViewImports.cshtml 里引入的命名空间和 TagHelper 也会自动包含在所有视图里。它们为什么会起作用呢?

_ViewImports 的秘密藏在 RazorTemplateEngine 类 和 MvcRazorTemplateEngine 类中。

MvcRazorTemplateEngine 类指明了 "_ViewImports.cshtml" 作为默认的名字。

// MvcRazorTemplateEngine.cs 部分代码// 完整代码: https://github.com/aspnet/Razor/blob/rel/2.0.0/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcRazorTemplateEngine.cs

public class MvcRazorTemplateEngine : RazorTemplateEngine{  

 public MvcRazorTemplateEngine(RazorEngine engine, RazorProject project)        : base(engine, project)    {Options.ImportsFileName = "_ViewImports.cshtml";Options.DefaultImports = GetDefaultImports();} }

RazorTemplateEngine 类则表明了 Razor 是如何去寻找 _ViewImports.cshtml 文件的。

// RazorTemplateEngine.cs 部分代码// 完整代码:https://github.com/aspnet/Razor/blob/rel/2.0.0/src/Microsoft.AspNetCore.Razor.Language/RazorTemplateEngine.cs
public class RazorTemplateEngine{  

 public virtual IEnumerable<RazorProjectItem> GetImportItems(RazorProjectItem projectItem)    {  
      var importsFileName = Options.ImportsFileName;    
          if (!string.IsNullOrEmpty(importsFileName)){          
           return Project.FindHierarchicalItems(projectItem.FilePath, importsFileName);}      
        return Enumerable.Empty<RazorProjectItem>();} }

FindHierarchicalItems 方法会返回一个路径集合,其中包括从视图当前目录一路到根目录的每一级目录下的 _ViewImports.cshtml 路径。换句话说,如果从根目录开始,到视图所在目录的每一层目录都有 _ViewImports.cshtml 文件的话,那么它们都会起作用。这也是为什么通常我们在 根目录下的 Views 目录里放一个 _ViewImports.cshtml 文件就会被所有视图文件所引用,因为 Views 目录是是所有视图文件的父/祖父目录。那么如果我们的 _ViewImports.cshtml 文件不在视图的目录层次结构中呢?

在这个 DI 为王的 ASP.NET Core 世界里,RazorTemplateEngine 也被注册为 DI 里的服务,因此我目前的做法继承 MvcRazorTemplateEngine 类,微调 GetImportItems 方法的逻辑,加入我们的特定路径,然后注册到 DI 取代原来的实现类型。代码如下:

// ModuleRazorTemplateEngine.cs
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;

namespace CustomizedViewLocation{    

public class ModuleRazorTemplateEngine : MvcRazorTemplateEngine{      
 public ModuleRazorTemplateEngine(RazorEngine engine, RazorProject project) : base(engine, project)        {}        
 
 public override IEnumerable<RazorProjectItem> GetImportItems(RazorProjectItem projectItem)        {IEnumerable<RazorProjectItem> importItems = base.GetImportItems(projectItem);          
  return importItems.Append(Project.GetItem($"/Shared/Views/{Options.ImportsFileName}"));}} }

然后在 Startup 类里把它注册到 DI 取代默认的实现类型。

// Startup.cs//
using Microsoft.AspNetCore.Razor.Language;

public void ConfigureServices(IServiceCollection services){services.AddSingleton<RazorTemplateEngine, ModuleRazorTemplateEngine>();IMvcBuilder mvcBuilder = services.AddMvc();    // 其它代码省略}

下面是 _ViewStart.cshtml 的问题了。不幸的是,Razor 对 _ViewStart.cshtml 的处理并没有那么“灵活”,看代码就知道了。

// RazorViewEngine.cs 部分代码// 完整代码:https://github.com/aspnet/Mvc/blob/rel/2.0.0/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs
public class RazorViewEngine : IRazorViewEngine{  

 private const string ViewStartFileName = "_ViewStart.cshtml";    internal ViewLocationCacheResult CreateCacheResult(        HashSet<IChangeToken> expirationTokens,      
         string relativePath,    
            bool isMainPage
)    
{    
    var factoryResult = _pageFactory.CreateFactory(relativePath);        var viewDescriptor = factoryResult.ViewDescriptor;    
        if (viewDescriptor?.ExpirationTokens != null){          
          for (var i = 0; i < viewDescriptor.ExpirationTokens.Count; i++){expirationTokens.Add(viewDescriptor.ExpirationTokens[i]);}}      
        if (factoryResult.Success){            // Only need to lookup _ViewStarts for the main page.var viewStartPages = isMainPage ?GetViewStartPages(viewDescriptor.RelativePath, expirationTokens) :Array.Empty<ViewLocationCacheItem>();      
            if (viewDescriptor.IsPrecompiled){_logger.PrecompiledViewFound(relativePath);}          
             return new ViewLocationCacheResult(          
                   new ViewLocationCacheItem(factoryResult.RazorPageFactory, relativePath),viewStartPages);}      
                     return null;}  
  
    private IReadOnlyList<ViewLocationCacheItem> GetViewStartPages(        string path,        HashSet<IChangeToken> expirationTokens)    {    
       var viewStartPages = new List<ViewLocationCacheItem>();        foreach (var viewStartProjectItem in _razorProject.FindHierarchicalItems(path, ViewStartFileName)){          
         var result = _pageFactory.CreateFactory(viewStartProjectItem.FilePath);            var viewDescriptor = result.ViewDescriptor;        
            if (viewDescriptor?.ExpirationTokens != null){            
               for (var i = 0; i < viewDescriptor.ExpirationTokens.Count; i++){expirationTokens.Add(viewDescriptor.ExpirationTokens[i]);}}          
            if (result.Success){                // Populate the viewStartPages list so that _ViewStarts appear in the order the need to be// executed (closest last, furthest first). This is the reverse order in which// ViewHierarchyUtility.GetViewStartLocations returns _ViewStarts.viewStartPages.Insert(0, new ViewLocationCacheItem(result.RazorPageFactory, viewStartProjectItem.FilePath));}}      
         return viewStartPages;} }

上面的代码里 GetViewStartPages 方法是个 private,没有什么机会让我们加入自己的逻辑。看了又看,好像只能从 _razorProject.FindHierarchicalItems(path, ViewStartFileName) 这里着手。这个方法同样在处理 _ViewImports.cshtml时用到过,因此和 _ViewImports.cshtml 一样,从根目录到视图当前目录之间的每一层目录的 _ViewStarts.cshtml 都会被引入。如果我们可以调整一下 FindHierarchicalItems 方法,除了完成它原本的逻辑之外,再加入我们对我们 /Shared/Views 目录的引用就好了。而 FindHierarchicalItems 这个方法是在 Microsoft.AspNetCore.Razor.Language.RazorProject 类型里定义的,而且是个 virtual 方法,而且它是注册在 DI 里的,不过在 DI 中的实现类型是 Microsoft.AspNetCore.Mvc.Razor.Internal.FileProviderRazorProject。我们所要做的就是创建一个继承自 FileProviderRazorProject 的类型,然后调整 FindHierarchicalItems 方法。

using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.Language;

namespace CustomizedViewLocation{  

 public class ModuleBasedRazorProject : FileProviderRazorProject{    
 
    public ModuleBasedRazorProject(IRazorViewEngineFileProviderAccessor accessor)            : base(accessor)        {}      
    
     public override IEnumerable<RazorProjectItem> FindHierarchicalItems(string basePath, string path, string fileName)        {IEnumerable<RazorProjectItem> items = base.FindHierarchicalItems(basePath, path, fileName);            // the items are in the order of closest first, furthest last, therefore we append our item to be the last item.return items.Append(GetItem("/Shared/Views/" + fileName));}} }

完成之后再注册到 DI。

// Startup.cs// using Microsoft.AspNetCore.Razor.Language;
public void ConfigureServices(IServiceCollection services){    // services.AddSingleton<RazorTemplateEngine, ModuleRazorTemplateEngine>(); // we don't need this any more if we make use of ModuleBasedRazorProjectservices.AddSingleton<RazorProject, ModuleBasedRazorProject>();IMvcBuilder mvcBuilder = services.AddMvc();    // 其它代码省略}

有了 ModuleBasedRazorProject 我们甚至可以去掉之前我们写的 ModuleRazorTemplateEngine 类型了,因为 Razor 采用相同的逻辑 —— 使用 RazorProject 的 FindHierarchicalItems 方法 —— 来构建应用 _ViewImports.cshtml 和 _ViewStart.cshtml 的目录层次结构。所以最终,我们只需要一个类型来解决问题 —— ModuleBasedRazorProject

回顾这整个思考和尝试的过程,很有意思,最终解决方案是自定义一个 RazorProject。是啊,毕竟我们的需求只是一个不同目录结构的 Razor Project,所以去实现一个我们自己的 RazorProject 类型真是再自然不过的了。

文本中的示例代码在这里 https://github.com/RickyLin/Demos/tree/master/CustomizedViewLocation 

相关文章: 

  • .NET Core 2.0 正式发布信息汇总

  • .NET Standard 2.0 特性介绍和使用指南

  • .NET Core 2.0 的dll实时更新、https、依赖包变更问题及解决

  • .NET Core 2.0 特性介绍和使用指南

  • Entity Framework Core 2.0 新特性

  • 体验 PHP under .NET Core

  • .NET Core 2.0使用NLog

  • 升级项目到.NET Core 2.0,在Linux上安装Docker,并成功部署

  • 解决Visual Studio For Mac Restore失败的问题

  • ASP.NET Core 2.0 特性介绍和使用指南

  • .Net Core下通过Proxy 模式 使用 WCF

  • .NET Core 2.0 开源Office组件 NPOI

  • ASP.NET Core Razor页面 vs MVC

  • Razor Page–Asp.Net Core 2.0新功能  Razor Page介绍

  • MySql 使用 EF Core 2.0 CodeFirst、DbFirst、数据库迁移(Migration)介绍及示例

  • .NET Core 2.0迁移技巧之web.config配置文件

  • asp.net core MVC 过滤器之ExceptionFilter过滤器(一)

  • ASP.NET Core 使用Cookie验证身份

  • ASP.NET Core MVC – Tag Helpers 介绍

  • ASP.NET Core MVC – Caching Tag Helpers

  • ASP.NET Core MVC – Form Tag Helpers

  • ASP.NET Core MVC – 自定义 Tag Helpers

  • ASP.NET Core MVC – Tag Helper 组件

  • ASP.Net Core Razor 页面路由

  • 粗略使用.NetCore2.0自带授权登陆Authorize

原文地址:http://www.cnblogs.com/Ricky81317/p/7483222.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

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

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

相关文章

Safari浏览器不支持……

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂前言现在是2022年1月7日16:19:38,前几天用publiccms改了个网站&#xff0c;因为客户那边各种机型都有&#xff08;各PC端的分辨率也都不一样&#xff09;&#xff0c;所以导致页面呈现的效果…

开源分享 Unity3d客户端与C#分布式服务端游戏框架

很久之前&#xff0c;在博客园写了一篇文章&#xff0c;《分布式网游server的一些想法语言和平台的选择》&#xff0c;当时就有了用C#做网游服务端的想法。写了个Unity3d客户端分布式服务端框架&#xff0c;最近发布了1.0版本&#xff0c;取名ET框架。ET框架的目标就是简化客户…

freemarker中遇到null报错的处理方法

错误分析 今天遇到了这样的个问题&#xff0c;就是在获取分类的父id的时候发现如果是父级分类&#xff0c;则回去父id就会报错。 直接导致了后面的样式失败。 解决办法&#xff1a; 给添加了个默认值0&#xff0c;就可以了&#xff0c;代码如下&#xff1a; var cate_pare…

IDEA的debug方法头坑

一、现象复现 web程序跑起来很卡顿&#xff0c;十几分钟都跑步起来&#xff0c;而且页面刷新十几秒都没有反应。 三月 23, 2019 11:58:22 上午 com.mchange.v2.log.MLog <clinit> 信息: MLog clients using java 1.4 standard logging. 三月 23, 2019 11:58:22 上午 co…

ASP.NET Core MVC I\/O编程模型

1.1. I/O编程模型浅析 服务器端编程经常需要构造高性能的IO模型&#xff0c;常见的IO模型有四种&#xff1a; &#xff08;1&#xff09;同步阻塞IO&#xff08;Blocking IO&#xff09;&#xff1a;即传统的IO模型。 &#xff08;2&#xff09;同步非阻塞IO&#xff08;Non…

几天没写代码,就……

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂”前言现在是2022年2月1日21:07:37&#xff0c;今天是农历2022年的第一天&#xff0c;祝大家虎年大吉&#xff0c;新的一年里身体健康&#xff0c;事业有成&#xff01;&#xff01;&#…

深入浅出 Java CMS 学习笔记

转载自 深入浅出 Java CMS 学习笔记 引子 带着问题去学习一个东西&#xff0c;才会有目标感&#xff0c;我先把一直以来自己对CMS的一些疑惑罗列了下&#xff0c;希望这篇学习笔记能解决掉这些疑惑&#xff0c;希望也能对你有所帮助。 1、 CMS出现的初衷、背景和目的&#x…

vue利用级联选择器实现全国省市区乡村五级菜单联动

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”现在是&#xff1a;2022年2月13日20:09:27今天分享一个五级级联地址的组件的使用吧。前言接到这样的一个需求&#xff1a;需要根据地址查询列表信息&#xff0c;地址可以分别按照省、市…

业务库负载翻了百倍,我做了什么来拯救MySQL架构

转载自 业务库负载翻了百倍&#xff0c;我做了什么来拯救MySQL架构 作者介绍 杨建荣&#xff0c;竞技世界资深DBA&#xff0c;前搜狐畅游数据库专家&#xff0c;Oracle ACE&#xff0c;YEP成员。拥有近十年数据库开发和运维经验&#xff0c;目前专注于开源技术、运维自动化和…

ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界

本系列文章从源码分析的角度来探索 ASP.NET Core 的运行原理&#xff0c;分为以下几个章节&#xff1a; ASP.NET Core 运行原理解剖[1]:Hosting ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍 ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成 IHttpContext…

都忘了自己还有一套房子了。。。

​自20年始来&#xff0c;一直租着我的房子。当时疫情刚刚有所好转&#xff0c;我把房子挂在58上&#xff0c;陆陆续续的有好多人问&#xff0c;不过都没有租&#xff0c;一来离我住的地方有点远&#xff0c;过去一次得个把小时的&#xff0c;人家要看房子不能及时过去。二来问…

Visual Studio2017 远程调试 Remote Debugger

前言 大家在使用vs打包后的文件部署到服务器后&#xff0c;有时候我们需要对线网的后台进行调试。但是它不像在VS中。这个时候我们该怎么调试呢&#xff1f; 微软想到了这一点&#xff0c;他们在 VS 中给我们提供了一个功能: Remote Debugger&#xff0c;要远程调试我们就需要…

对于这款APP,我充了个终身VIP!!!

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”现在是&#xff1a;2022年3月15日00:02:07昨天&#xff0c;无意间知道了一款特牛的APP&#xff0c;看了看&#xff0c;决定充了个终身会员&#xff01;今天&#xff0c;我就简单的对这…

Ubuntu amp;amp; GitLab CI amp;amp; Docker amp;amp; ASP.NETnbs

上一篇&#xff1a;Ubuntu & GitLab CI & Docker & ASP.NET Core 2.0 自动化发布和部署&#xff08;1&#xff09; 服务器版本 Ubuntu 16.04 LTS。 本篇博文目的&#xff1a;项目中添加Dockerfile文件&#xff0c;使用 CI 脚本构建自定义镜像&#xff0c;推送到 …

新版《Windows Sysinternals实战指南》,读书积赞活动

新书《Windows Sysinternals实战指南》即将上市。该本由Sysinternals创始人、Windwos内核技术专家Mark Russinovich 与 Windows专家Aaron Margosis联手编著&#xff0c;详细介绍了Sysinternals每款工具的独到功能&#xff0c;并用较多篇幅深入介绍了如何通过几款重量级工具优化…

Bladex生成Swagger的方法

一、在启动类中添加如下代码&#xff1a;&#xff08;目的是为了打印输出swagger的地址等&#xff09; 注解&#xff1a;Slf4j 实现接口&#xff1a;CommandLineRunner 依赖注入&#xff1a; Autowiredprivate Environment environment;Overridepublic void run(String... str…

了解spark计算模型

简介 在集群背后&#xff0c;有一个非常重要的分布式数据架构&#xff0c;即弹性分布式数据集&#xff08;resilient distributed dataset&#xff0c;RDD&#xff09;&#xff0c;它是逻辑集中的实体&#xff0c;在集群中的多台机器上进行了数据分区。通过对多台机器上不同RDD…

如何理解事件溯源

在近期举行的PHPDublin见面会上&#xff0c;来自DynamicRes的架构师Barry Sullivan被问到“什么是事件溯源”&#xff0c;作为对这个问题的回答&#xff0c;他在博客上写下了这篇文章&#xff0c;详细解释了什么是事件溯源以及事件溯源有哪些好处。以下内容翻译自Barry的博客&a…

java中日期处理的一些工具方法

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 前言 现在是2022年4月16日15:35:14&#xff01;忙里偷闲&#xff0c;直接来看方法吧&#xff0c;写完文章继续去改Bug: 1.计算两个日期之间相差的天数 /*** param stratTime 开始时间* p…

ASP.NET Core 运行原理解剖[5]:Authentication

在现代应用程序中&#xff0c;认证已不再是简单的将用户凭证保存在浏览器中&#xff0c;而要适应多种场景&#xff0c;如App&#xff0c;WebAPI&#xff0c;第三方登录等等。在 ASP.NET 4.x 时代的Windows认证和Forms认证已无法满足现代化的需求&#xff0c;因此在ASP.NET Core…