• ASP.NET Core 2.2上使用Environment.CurrentDirectory获取根目录的问题
  • 发布于 2个月前
  • 336 热度
    0 评论
  • 酸梅汤
  • 0 粉丝 8 篇博客
  •   

ASP.NET Core 2.2 推出已经有一段时间了,其中有个新功能,能够使用新的AspNetCoreModuleV2并且在IIS上使用InProcess模式部署,以大幅提高性能。这几天Azure App Service终于完成了这个新版模块的部署,我第一时间将我的系统配置到新模块上,结果爆了。我们来看看原因和解决方式。


如果不知道什么是InProcess模式的话,简单来说,就是原先ASP.NET Core确实可以跑在IIS上,但其实是由一个名为AspNetCoreModule的IIS模块调用dotnet.exe启动kestrel来跑的,所以进程名字实际上是dotnet.exe。

而ASP.NET Core 2.2里新增了InProcess模式,可以在IIS自己的w3wp进程中跑你的应用。这个InProcess的In也就是In在了w3wp里的意思。据官网的描述,这种运行方式可以有400%的性能提升。

有兴趣可以看看官网的详细介绍:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/index?view=aspnetcore-2.2&tabs=windows#in-process-hosting-model


生产环境大爆炸
根据原先对ASP.NET Core的了解,我开发时候一般不会使用IIS去测试,用kestrel在开发环境测完以后直接使用Azure DevOps部署到Azure App Service上跑,结果今天升级到InProcess模式以后,生产环境爆了,而开发机的kestrel怎么弄都是好的。

启动失败,我的整个网站无法访问,好牛逼啊!


故障分析
还好微软智慧云Azure提供的全球独一无二的kudu工具可以非常方便的看到日志,日志显示:

[2018-12-26 12:06:26.5616][RD00155DB8C92A][Fatal][Microsoft.AspNetCore.Hosting.Internal.WebHost] Application startup exception System.IO.FileNotFoundException: Could not find file 'D:\Windows\system32\urlrewrite.xml'.
File name: 'D:\Windows\system32\urlrewrite.xml'


怎么会这样?我的代码访问的明明是应用根目录下的文件,为何跑到系统目录去了?再一看启动日志:

[2018-12-26 12:06:23.7946][RD00155DB8C92A][Info][Moonglade.Web.Program] Moonglade is starting, hail Microsoft! 
--------------------------------------------------------
 Version: 10.0.6934.1000 
 Directory: D:\Windows\system32 
 x64Process: True 
 OSVersion: Microsoft Windows 10.0.14393  
 AppDomain: Moonglade.Web 
 UserName: moonglade 
-------------------------------------------------------- 


故障代码是这段:

using (var sr = File.OpenText("urlrewrite.xml"))
{
    ...
}

最终发现,在Kestrel下运行的时候,Environment.CurrentDirectory指向的是应用根目录,而在IIS的InProcess模式下运行的时候,则指向系统目录,最终导致应用里只要间接或直接使用Environment.CurrentDirectory的代码,都会爆。


解决办法

我们可以利用IHostingEnvironment接口里提供的ContentRootPath属性来获取当前应用目录的绝对路径,这是在Kestrel和IIS中行为一致的。我的故障代码正好位于可以访问到IHostingEnvironment的地方,因此做如下修改:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    string baseDir = env.ContentRootPath;
    using (var sr = File.OpenText(Path.Combine(baseDir, "urlrewrite.xml")))
    {
        ...
    }
}

对于没法直接访问IHostingEnvironment又懒得做依赖注入的地方,可以这么玩:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    string baseDir = env.ContentRootPath;
    ...
    AppDomain.CurrentDomain.SetData(Constants.AppBaseDirectory, baseDir);
}

然后在要用的地方GetData()就好了:
var configSource = $@"{AppDomain.CurrentDomain.GetData(Constants.AppBaseDirectory)}\mailConfiguration.xml";
再次部署上线,问题成功解决!


其他注意事项
如果你在VS里使用IIS去debug,比如这样设置的话:

会产生一个debug配置的web.config文件,而这个文件在默认情况下会参与你CI/CD环境的编译和发布,最终导致你指定用release模式编译的网站,上线之后是debug标记的。一定记得要手工排除这个文件。
ASP.NET Core 2.2 以后,再也不能想当然地认为开发ASP.NET Core 用不用IIS都无所谓了,真的是有些东西在IIS和Kestrel下行为不一致,因此推荐大家如果生产环境在使用IIS的话,上线之前务必在本地的IIS上用同样配置测试一遍。

用户评论