在 .NET Core 5 中集成 Create React app

翻译自 Camilo Reyes 2021年2月22日的文章 《Integrate Create React app with .NET Core 5》 [1]

 

本文演示了如何将 Create React app 与 .NET Core 集成,以生成一个移除了几个依赖项的脚手架。

Create React app 是社区中创建一个全新 React 项目的首选方式。该工具生成了基础的脚手架用于开始编写代码,并抽象出了许多具有挑战性的依赖项。webpack 和 Babel 之类的 React 工具被集中到一个单独的依赖项中,使得 React 开发者可以专注于手头的工作,这降低了构建单页应用的必要门槛。

不过问题依然存在,虽然 React 解决了客户端的问题,但服务端呢?.NET 开发者在使用 Razor、服务器端配置,并通过 session cookie 处理 ASP.NET 用户会话(session)方面有着悠久的历史。在本文中,我将向您展示如何通过两者之间的良好集成来实现两全其美的效果。

本文提供了一种动手实践的方式,因此您可以依照自上而下的顺序,获得更佳的阅读效果。如果您更喜欢随着代码学习,可以从 GitHub 上获取源码[2],使阅读更愉快。

一般的解决方案涉及两个主要部分——前端和后端。后端是一个典型的 ASP.NET MVC 应用,任何人都可以在 .NET Core 5 中启动。请确保您已安装 .NET Core 5,并将项目的目标设置为 .NET Core 5,只要执行了此操作也便开启了 C# 9 特性。随着集成的进行,我还将添加更多的部分。前端会有 React 项目和输出像 index.html 之类静态资产的 NPM 工具。我将假定您具有 .NET 和 React 的工作知识,因此我不会深究诸如在开发机上设置 .NET Core 或 Node 的基础。也就是说,请注意下面一些有用的 using 语句,以便后面使用:

using Microsoft.AspNetCore.Http;
using System.Net;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using System.Text.RegularExpressions;

初始化项目设置

好消息是,微软提供了一个基础的脚手架模板,用于启动新的带有 React 前端的 ASP.NET 项目。该 ASP.NET React 项目具有一个客户端应用,它输出可以托管在任何地方的静态资产;以及一个 ASP.NET 后端应用,它可以通过调用 API Endpoints 获取 JSON 数据。这里的一个优点是,整个解决方案可以作为一个整体同时部署,而无需将前后两端拆分成单独的部署管道。

要安装基础的脚手架,请执行以下操作:

mkdir integrate-dotnet-core-create-react-app
cd integrate-dotnet-core-create-react-app
dotnet new react --no-https
dotnet new sln
dotnet sln add integrate-dotnet-core-create-react-app.csproj

有了这些,就可以在 Visual Studio 或 VS Code 中打开解决方案文件。您可以运行 dotnet run 来启动项目,看看该脚手架都为您做了些什么。请注意命令 dotnet new react,这是我用于该 React 项目的模板。

下面是初始模板的样子:

如果您在使用 React 时遇到任何问题,只需将目录更改为 ClientApp 并运行 npm install,即可启动并运行 Create React App。整个 React 应用程序在客户端渲染,而不需要服务器渲染。它有一个具有三个不同页面的 react-router:一个计数器、一个获取天气数据的页面和一个主页。如果您看一下控制器,会发现 WeatherForecastController 有一个 API Endpoint 来获取天气数据。

该脚手架已经包含了一个 Create React App 项目。为了验证这一点,请打开 ClientApp 文件夹中的 package.json 文件进行检查。

这就是它的证据:

{"scripts": {"start": "rimraf ./build && react-scripts start","build": "react-scripts build",}
}

找到 react-scripts,这是像 webpack 一样封装所有其他 React 依赖项的单一依赖项。若要在将来升级 React 和它的依赖项,您只需升级这一依赖项。它抽象化了可能有潜在危险的升级以保持最新状态,因此这才是 React App 的真正魔力。

ClientApp 中的整个文件夹结构遵循常规的 Create React App 项目,在其周围包裹着 ASP.NET 项目。

文件夹结构如下所示:

该 React 应用程序有很多优点,但是它缺少一些重要的 ASP.NET 功能:

  • 没有通过 Razor 进行的服务端渲染,使任何其他 MVC 页面像一个单独的应用程序一样工作

  • 很难从 React 客户端访问 ASP.NET 服务端配置数据

  • 它不会集成由 session cookie 实现的 ASP.NET 用户会话

随着集成的推进,我将逐一解决这些问题。好在这些理想的功能是可以使用 Create React App 和 ASP.NET 实现的。

为了跟踪集成更改,我将使用 Git 提交初始脚手架。假设 Git 已安装,请执行 git initgit add 和 git commit 来提交这个初始项目。查看提交历史是跟踪集成所需更改的一种很好的方法。

现在,创建以下对此集成很有用的文件夹和文件。我建议使用 Visual Studio 右键单击创建控制器 、类或 View:

  • /Controllers/HomeController.cs:服务端主页,它将覆盖 Create React App 的 index.html 入口页

  • /Views/Home/Index.cshtml:Razor 视图,它渲染服务端组件和来自 React 项目的解析过的 index.html

  • /CreateReactAppViewModel.cs:主要的集成视图模型,它将抓取 index.html 静态资产并将其解析出来以供 MVC 使用

有了这些文件夹和文件后,请终止当前正在运行的应用程序,并通过 dotnet watch run 以监视模式启动该应用程序。此命令跟踪前端和后端的更改,甚至在需要时刷新页面。

其余的必要更改将放入脚手架现有的文件中。这好极了,因为可以最大限度地减少必要的代码调整来充实这个集成。

是时候擼起袖子,做个深呼吸,处理这个集成的主要部分了。

CreateReactAppViewModel 集成

我将从创建一个执行大部分集成工作的视图模型开始。打开 CreateReactAppViewModel 并放入以下代码:

public class CreateReactAppViewModel
{private static readonly Regex _parser = new(@"<head>(?<HeadContent>.*)</head>\s*<body>(?<BodyContent>.*)</body>",RegexOptions.IgnoreCase | RegexOptions.Singleline);public string HeadContent { get;set;}public string BodyContent { get;set;}public CreateReactAppViewModel(HttpContext context){var request = WebRequest.Create(context.Request.Scheme + "://" + context.Request.Host +context.Request.PathBase + "/index.html");var response = request.GetResponse();var stream = response.GetResponseStream();var reader = new StreamReader(stream ?? throw new InvalidOperationException("The create-react-app build output could not be found in " +"/ClientApp/build. You probably need to run npm run build. " +"For local development, consider npm start."));var htmlFileContent = reader.ReadToEnd();var matches = _parser.Matches(htmlFileContent);if (matches.Count != 1){throw new InvalidOperationException("The create-react-app build output does not appear " +"to be a valid html file.");}var match = matches[0];HeadContent = match.Groups["HeadContent"].Value;BodyContent = match.Groups["BodyContent"].Value;}
}

这段代码乍一看可能有点吓人,但它只做了两件事:从开发服务器获取输出的 index.html 文件,并解析出 head 和 body 标签。这使得 ASP.NET 中的消费方应用程序可以访问 HTML,该 HTML 链接到来自 Create React App 的静态资产。这些资产是静态文件,其中包含带有 JavaScript 和 CSS 的代码包。例如,JavaScript 包 js\main.3549aedc.chunk.js 或 CSS 包 css\2.ed890e5e.chunk.css。这就是在 React 中 webpack 接收所编写的代码并将其呈现到浏览器的方式。

我选择直接向开发服务器发起一个 WebRequest,是因为在开发模式下,Create React App 不会生成 ASP.NET 可访问的任何实际文件。这对于本地开发来说足够了,因为它可以与 webpack 开发服务器很好地配合。客户端上的任何更改都将自动更新到浏览器。在监视模式下进行的任何后端更改也会刷新到浏览器。因此,您可以在两全其美的环境中获得最佳的生产力。

在生产环境中,将会通过 npm run build 创建静态资产。我建议执行文件 IO,并从 ClientApp/build 中的实际位置读取 index 文件。另外,在生产模式下,最好在静态资产部署到托管服务器之后缓存该文件的内容。

为了让您有一个更好的概念,下面是一个 build 后的 index.html 文件的样子:

我高亮显示了消费方 ASP.NET 应用需要解析的 head 和 body 标签。有了这些原始的 HTML,剩下的就简单多了。

视图模型就绪后,就该花点时间处理 home 控制器了,它将覆盖来自 React 的 index.html

打开 HomeController 并添加以下代码:

public class HomeController : Controller
{public IActionResult Index(){var vm = new CreateReactAppViewModel(HttpContext);return View(vm);}
}

在 ASP.NET 中,该控制器是默认路由,它会在服务端渲染的支持下覆盖 Create React App。这就是解锁集成的诀窍,从而可以两全其美。

接着,把下面的 Razor 代码放入 Home/Index.cshtml 中:

@model integrate_dotnet_core_create_react_app.CreateReactAppViewModel<!DOCTYPE html>
<html lang="en">
<head>@Html.Raw(Model.HeadContent)
</head>
<body>@Html.Raw(Model.BodyContent)<div class="container "><h2>Server-side rendering</h2></div>
</body>
</html>

React 应用程序使用 react-router 来定义客户端的路由。如果在浏览器处于非 home 路由时刷新页面,它将恢复为静态的 index.html

要解决这种不一致性,请在 Startup 中定义下面的服务端路由,路由是在 UseEndpoints 中定义的:

endpoints.MapControllerRoute("default","{controller=Home}/{action=Index}");
endpoints.MapControllerRoute("counter","/counter",new { controller = "Home", action = "Index"});
endpoints.MapControllerRoute("fetch-data","/fetch-data",new { controller = "Home", action = "Index"});

此时,看一下浏览器,现在它将通过 h2 显示这个服务端“组件”。这看起来似乎有点愚蠢,因为它只是在页面上渲染的一些简单 HTML,但其潜力是无穷的。ASP.NET Razor 页面可以具有完整的应用程序外壳,其中包含菜单、品牌和导航,它可以在多个 React 应用之间共享。如果有任何旧版 MVC Razor 页面,这个闪亮的新 React 应用能够无缝集成。

服务器端应用程序配置

接下来,假如我想显示此应用上来自 ASP.NET 的服务端配置,比如 HTTP 协议、主机名和 base URL。我选择这些主要是为了保持简单,不过这些配置值可以来自任何地方,它们可以是 appsettings.json 设置,或者甚至可以是来自配置数据库中的值。

要使服务端设置可以被 React 客户端访问,请将其放在 Index.cshtml 中:

<script>window.SERVER_PROTOCOL = '@Context.Request.Protocol';window.SERVER_SCHEME = '@Context.Request.Scheme';window.SERVER_HOST = '@Context.Request.Host';window.SERVER_PATH_BASE = '@Context.Request.PathBase';
</script><p>@Context.Request.Protocol@Context.Request.Scheme://@Context.Request.Host@Context.Request.PathBase
</p>

这里在全局 window 浏览器对象中设置来自服务器的任意配置值。React 应用可以轻而易举地检索这些值。我选择在 Razor 中渲染这些相同的值,主要是为了演示它们与客户端应用将看到的是相同的值。

在 React 中,打开 components\NavMenu.js 并添加下面的代码段;其中大部分将放在 Navbar 中:

import { NavbarText } from 'reactstrap';<NavbarText>{window.SERVER_PROTOCOL}{window.SERVER_SCHEME}://{window.SERVER_HOST}{window.SERVER_PATH_BASE}
</NavbarText>

这个客户端应用现在将显示通过全局 window 对象设置的服务器端配置。它不需要触发 Ajax 请求来加载这些数据,也不需要以某种方式让 index.html 静态资产可以访问它。

假如您使用了 Redux,这会变得更加容易,因为可以在应用程序初始化 store 时进行设置。初始化状态值可以在客户端渲染任何内容之前传递到 store 中。

例如:

const preloadedState = {config: {protocol: window.SERVER_PROTOCOL,scheme: window.SERVER_SCHEME,host: window.SERVER_HOST,pathBase: window.SERVER_PATH_BASE}
};const store = createStore(reducers, preloadedState, applyMiddleware(...middleware));

为了简洁起见,我选择不使用 Redux store,而是通过 window 对象的方式实现,这只是一个粗略的想法。这种方法的好处是,整个应用都可以保持单元可测试的状态,而不会受到类似 window 对象的浏览器依赖项的污染。

.NET Core 用户会话集成

最后,作为主菜,现在我将这个 React 应用与 ASP.NET 用户会话(Session)集成在一起。我将锁定获取天气数据的后端 API,并仅在使用有效会话时显示此信息。这意味着当浏览器触发 Ajax 请求时,它必须包含一个 ASP.NET session cookie。否则,该请求将被拒绝,并重定向以指示浏览器必须先登录。

要在 ASP.NET 中启用用户会话支持,请打开 Startup 文件并添加:

public void ConfigureServices(IServiceCollection services)
{services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>{options.Cookie.HttpOnly = true;});
}public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{// 将下面代码放在 UseRouting 和 UseEndpoints 之间app.UseAuthentication();app.UseAuthorization();
}

请务必保留其余的脚手架代码,只是在恰当的方法中添加上面的代码段。启用了身份验证和授权后,转到 WeatherForecastController 并给该控制器添加一个 Authorize 属性。这将有效地将其锁定,从而需要由 cookie 实现的 ASP.NET 用户会话来获取数据。

Authorize 属性假定用户可以登录到该应用。回到 HomeController 并添加 Login 和 Logout 方法,记得添加 using Microsoft.AspNetCore.AuthenticationMicrosoft.AspNetCore.Authentication.Cookies 和 Microsft.AspNetCore.Mvc

这是建立然后终止用户会话的一种方法:

public async Task<ActionResult> Login()
{var userId = Guid.NewGuid().ToString();var claims = new List<Claim>{new(ClaimTypes.Name, userId)};var claimsIdentity = new ClaimsIdentity(claims,CookieAuthenticationDefaults.AuthenticationScheme);var authProperties = new AuthenticationProperties();await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,new ClaimsPrincipal(claimsIdentity),authProperties);return RedirectToAction("Index");
}public async Task<ActionResult> Logout()
{await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);return RedirectToAction("Index");
}

请注意,通常使用重定向和 ASP.NET session cookie 来建立用户会话。我添加了一个 ClaimsPrincipal,它带有一个设置为随机 Guid 的用户 ID,使其看起来更加真实。[3] 在实际应用中,这些 Claims 可能来自数据库或者 JWT。

要将此功能公开给客户端,请打开 components\NavMenu.js 并将下面的链接添加到 Navbar

<NavItem><a class="text-dark nav-link" href="/home/login">Log In</a>
</NavItem>
<NavItem><a class="text-dark nav-link" href="/home/logout">Log Out</a>
</NavItem>

最后,我希望客户端应用处理请求失败的情况,并给最终用户提供一些提示,指出出了点问题。打开 components\FetchData.js 并用下面的代码段替换 populateWeatherData

async populateWeatherData() {try {const response = await fetch('weatherforecast',{ redirect: 'error' });const data = await response.json();this.setState({ forecasts: data, loading: false });} catch {this.setState({forecasts: [{ date: 'Unable to get weather forecast' }],loading: false});}
}

我调整了一下 fetch,以使它不会用重定向跟踪失败的请求,而是返回一个错误响应。当 Ajax 请求获取数据失败时,ASP.NET 中间件将以重定向到登录页的方式响应。在实际的应用中,我建议将其自定义为 401 (Unauthorized) 状态码,以便客户端可以更优雅地处理此问题;或者,设置某种方式来轮询后端并检查活动会话,然后通过 window.location 进行相应地重定向。

完成后,dotnet 监视程序应该会在刷新浏览器时跟踪前后两端的更改。为了进行测试,我将首先访问 Fetch Data 页,请注意会请求失败,然后登录,并使用有效的会话再次尝试获取天气数据。我将打开“Network”选项卡,以在浏览器中显示 Ajax 请求。

请注意当我第一次获取天气数据时的 302 重定向,它失败了。接着,来自登录页的后续重定向建立了一个会话。查看一下浏览器的 cookies,会显示这个名为 AspNetCore.Cookies 的 cookie,它是一个 session cookie,正是它让后面的 Ajax 请求工作正常了。

结论

.NET Core 5 和 React 不必独立存在。通过出色的集成,便可以在 React 中解锁服务端渲染、服务端配置数据和 ASP.NET 用户会话。


相关链接:

  1. https://www.red-gate.com/simple-talk/dotnet/net-tools/integrate-create-react-app-with-net-core-5/ Integrate Create React app with .NET Core 5 ↩︎

  2. https://github.com/beautifulcoder/integrate-dotnet-core-create-react-app.git 示例代码 ↩︎

  3. 用 Cookie 代表一个通过验证的主体,它包含 Claims, ClaimsIdentity, ClaimsPrincipal 三部分信息,其中 ClaimsPrincipal 相当于持有证件的人,ClaimsIdentity 就是持有的证件,Claims 是证件上的信息。https://andrewlock.net/introduction-to-authentication-with-asp-net-core/ ↩︎

作者 :Camilo Reyes  
译者 :技术译民  
出品 :技术译站(https://ITTranslator.cn/)

END

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

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

相关文章

程序员找不到对象是因为还没遇到一个有远见的丈母娘

当别人在放肆秀恩爱的时候&#xff0c;程序员单身狗们在角落里瑟瑟发抖。别人去网站相亲找到对象&#xff0c;程序员去相亲找到BUG。其实&#xff0c;你找不到对象是因为你还没遇到一个有远见的丈母娘。都说程序员很难找到对象&#xff0c;就知道整天对着键盘一直敲敲敲&#x…

axios请求拦截器错误_Axios使用拦截器全局处理请求重试

Axios拦截器Axios提供了拦截器的接口&#xff0c;让我们能够全局处理请求和响应。Axios拦截器会在Promise的then和catch调用前拦截到。请求拦截示例axios.interceptors.request.use(function (config) {// 在发起请求请做一些业务处理return config;}, function (error) {// 对…

第十一章项目沟通管理重点--转载

《信息系统项目管理师教程》第十一章项目沟通管理重点 11&#xff0e;1项目沟通管理的重要性 1、编码、通信、传播媒介、噪声、解码。确认是接收到信号&#xff0c;但并不需要理解&#xff0c;而反馈信息指读懂信息&#xff0c;解码并回复。 2、沟通障碍&#xff1a;认识障碍、…

WPF 如何将IconFont图标转成Geometry

之前每次使用IconFont图标&#xff0c;都要去下载一个png图片&#xff0c;每次颜色什么的改了&#xff0c;都要重新下载&#xff0c;太苦逼了。现在好了&#xff0c;终于找到如何方便快速地使用IconFont图标了。是应该的演示如何从IconFont网站上找到Geometry先看看效果吧&…

给所有想从事软件研发的年轻工程师的忠告与建议

图片来源&#xff1a;Fargo Season 3一、我为什么写这篇文章&#xff1a;这几天&#xff0c;在某个IT论坛的软件培训与认证栏目中&#xff0c;看到了很多处于迷惑之中的人们&#xff0c;也看到了许多大家普遍感到困惑的问题&#xff0c;写此文章的目的&#xff0c;是想将我这些…

谁今天收到鸿蒙系统推送,鸿蒙系统正式推送,只有部分高端机才能收到

原标题&#xff1a;鸿蒙系统正式推送&#xff0c;只有部分高端机才能收到华为已经对鸿蒙2.0系统开始进行推送更新&#xff0c;从华为推出鸿蒙系统概念已经时隔几年了&#xff0c;如今华为把ppt系统映射进现实是真正为自己正名了&#xff0c;而且据华为陈述鸿蒙2.0已经能达到安卓…

layui导入模板数据_java+layui的Excel导入导出

//导入PostMapping(value "importData")ResultJson importData(RequestParam MultipartFile file) {ResultJson resultJson newResultJson();List importData null;try{importData ExcelUtil.importExcel(file.getInputStream(), ProjectJson.class);}catch(IOExce…

入门基础-VC网络编程入门

对于许多初学者来说&#xff0c;网络通信程序的开发&#xff0c;普遍的一个现象就是觉得难以入手。许多概念&#xff0c;诸如&#xff1a;同步(Sync)/异步(Async)&#xff0c;阻塞(Block)/非阻塞(Unblock)等&#xff0c;初学者往往迷惑不清&#xff0c;只知其所以而不知起所以然…

NET问答: 如何按属性进行 Distinct() ?

咨询区 Patrick Desjardins&#xff1a;我现在正在学习 LINQ&#xff0c;对一个简单类型的 List 进行 Distinct() 是非常简单的&#xff0c;如 List<int> 或 List<string>&#xff0c;但如果对复杂类型 List<T> 的某一个或者多个属性进行 Distinct() 的话&am…

html站点如何命名,html页面的CSS、DIV命名规则

68. XHTML文件中id的命名(1)页面结构容器: container页头&#xff1a;header内容&#xff1a;content/container页面主体&#xff1a;main页尾&#xff1a;footer导航&#xff1a;nav侧栏&#xff1a;sidebar栏目&#xff1a;column页面外围控制整体布局宽度&#xff1a;wra…

2020邮箱账号密码大全_通知 | 复旦大学2020年春季学期研究生选课FAQ

1选课须知1. 研究生选课系统什么时间开放&#xff1f; 答&#xff1a;2020年春季学期研究生选课系统开放时间为&#xff1a;2020年2月19日(周三)10:00至2020年3月9日(周一)10:00。2020年3月9日(周一)10:00后&#xff0c;研究生可以在选课系统中查询课表、已选课程、学分获得情况…

如何从零开始构建深度学习项目?这里有一份详细的教程

导读&#xff1a;在学习了有关深度学习的理论之后&#xff0c;很多人都会有兴趣尝试构建一个属于自己的项目。本文将会从第一步开始&#xff0c;告诉你如何解决项目开发中会遇到的各类问题。本文由六大部分组成&#xff0c;涵盖深度学习 ( DL ) 项目的整个过程。我们将使用一个…

性能分布式NewLife.XCode对无限数据的支持

上周发布了《改进版CodeTimer及XCode性能测试》&#xff0c;展示了NewLife.XCode在性能上的表现。实际上NewLife.XCode是一个很平凡的ORM&#xff0c;只是在分页和缓存方面多下点功夫&#xff0c;注意每一个细节&#xff0c;才能保证在数据量大、业务繁忙的环境中得以保持良好的…

C#实用小知识:string和判断null

stringstring是常用的类型&#xff0c;它具有不可变性&#xff1a;就是一旦赋值&#xff0c;就不可变&#xff0c;如果再赋值 &#xff0c;就重新开辟内存空间&#xff1b;保留性&#xff1a;如果一个字符串存在&#xff0c;另一个与其相同&#xff0c;他们会指向同一个地址&am…

s6730堆叠_不再只是堆叠硬件 ivvi S6全方位评测

【PConline 评测】ivvi是酷派公司的又一个全新的子品牌&#xff0c;与旗下的电商品牌“大神”相比&#xff0c;ivvi的定位更为高端&#xff0c;动用酷派公司的顶级资源打造&#xff0c;通过线上线下一体销售。在机身做工设计与硬件选用方面ivvi尽善尽美&#xff0c;集合了酷派手…

.net html5页面缓存,详解HTML5中的manifest缓存使用

起源html5之前的网页&#xff0c;都是无连接&#xff0c;必须联网才能访问&#xff0c;这其实也是web的特色&#xff0c;这其实对于PC是时代问题并不大&#xff0c;但到了移动互联网时代&#xff0c;设备终端位置不再固定&#xff0c;依赖无线信号&#xff0c;网络的可靠性变得…

机器学习的最佳学习路线原来只有四步

AI这个词相信大家都非常熟悉&#xff0c;近几年来人工智能圈子格外热闹&#xff0c;光是AlphoGo就让大家对它刮目相看。今天小天就来跟大家唠一唠如何进军人工智能的第一步——机器学习。在机器学习领域&#xff0c;Python已经成为了主流。一方面因为这门语言简单易上手&#x…

AutoCAD VBA基于对象的分层

AutoCAD VBA基于对象的分层&#xff0c;讲不同对象根据特性分层&#xff0c;代码如下。 Dim Value As Variant Value ThisDrawing.GetVariable("cmdecho") ThisDrawing.SetVariable "cmdecho", 0 Dim ObjLayer As AcadLayer Set ObjLayer ThisDrawing.La…

ASP.NET Core Filter如何支持依赖注入

概述通过使用 ASP.NET Core 中的筛选器&#xff0c;可在请求处理管道中的特定阶段之前或之后运行代码。内置筛选器处理任务&#xff0c;例如&#xff1a;授权&#xff08;防止用户访问未获授权的资源&#xff09;。响应缓存&#xff08;对请求管道进行短路出路&#xff0c;以便…

做流向图_各类型供热暖系统图大全,一饱眼福!

↑ 点击上方“暖通风向标”关注我们推广.暖通风向标本文来源&#xff1a;制冷网好书推荐做热泵这几本书你不得不看&#xff01;区域供热系统热电联产系统地热水供暖系统即热式生活热水系统即热式特点:可保证用户随时随地均有热水供应&#xff0c;系统紧凑&#xff0c;无需储罐&…