此问题与App_Offline in MSBuild Remote Web Deploy 重复,但我已将答案粘贴在下面。 MSDeploy v3 直接支持 app_offline,但双方都需要 MSDeploy v3。我们尚未更新 VS Web Publish 体验以便能够利用这一点,但我们会在某个时候进行更新。
我最近刚刚在http://sedodream.com/2012/01/08/HowToTakeYourWebAppOfflineDuringPublishing.aspx 上写了一篇博客。它比它应该的更困难,我正在努力为以后的版本简化它。无论如何,我已经为您粘贴了所有内容。
我收到了一封客户电子邮件,询问他们如何在 Visual Studio 发布的整个持续时间内使他们的 Web 应用程序/站点脱机。使您的站点脱机的一种简单方法是将 app_offline.htm 文件放在站点根目录中。有关这方面的更多信息,您可以阅读 ScottGu 的帖子,链接在下面的资源部分。不幸的是,Web Deploy 本身不支持这个。如果您希望 Web Deploy(又名 MSDeploy)原生支持此功能,请在 http://aspnet.uservoice.com/forums/41199-general/suggestions/2499911-take-my-site-app-offline-during-publishing 投票。
由于 Web Deploy 不支持这一点,它会有点困难,它需要我们执行以下步骤:
- 发布 app_offline.htm
- 发布应用,并确保 app_offline.htm 包含在要发布的负载中
- 删除 app_offline.htm
1 将在发布过程开始之前使应用离线。
2 将确保在我们发布时 app_offline.htm 不会被删除(从而使应用保持离线状态)
3 将删除 app_offline.htm 并使网站恢复在线
现在我们知道需要做什么,让我们看看实现。首先是简单的部分。在您的 Web 应用程序项目 (WAP) 中创建一个名为 app_offline-template.htm 的文件。这将是最终成为目标服务器上的 app_offline.htm 文件的文件。如果您将其留空,您的用户将收到一条通用消息,说明应用程序已离线,但您最好将 static HTML(无 ASP.NET 标记)放置在该文件中,让用户知道该站点将恢复,以及您认为与您的用户相关的任何其他信息。添加此文件时,您应该在“属性”网格中将“构建操作”更改为“无”。这将确保此文件本身未发布/打包。由于文件以 .htm 结尾,默认情况下会发布。见下图。
现在是最难的部分。对于 Web 应用程序项目,我们有一个挂钩到发布/打包过程,我们称之为“wpp.targets”。如果您想扩展您的发布/打包过程,您可以在与项目文件本身相同的文件夹中创建一个名为 {ProjectName}.wpp.targets 的文件。这是我创建的文件,您可以将内容复制并粘贴到您的 wpp.targets 文件中。我将解释重要部分,但想发布整个文件以说服您。注意:你可以从我的 github repo 中获取这个文件的最新版本,链接在下面的资源部分。
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="InitalizeAppOffline">
<!--
This property needs to be declared inside of target because this is imported before
the MSDeployPath property is defined as well as others -->
<PropertyGroup>
<MSDeployExe Condition=" '$(MSDeployExe)'=='' ">$(MSDeployPath)msdeploy.exe</MSDeployExe>
</PropertyGroup>
</Target>
<PropertyGroup>
<PublishAppOfflineToDest>
InitalizeAppOffline;
</PublishAppOfflineToDest>
</PropertyGroup>
<!--
%msdeploy%
-verb:sync
-source:contentPath="C:\path\to\app_offline-template.htm"
-dest:contentPath="Default Web Site/AppOfflineDemo/app_offline.htm"
-->
<!--***********************************************************************
Make sure app_offline-template.htm gets published as app_offline.htm
***************************************************************************-->
<Target Name="PublishAppOfflineToDest"
BeforeTargets="MSDeployPublish"
DependsOnTargets="$(PublishAppOfflineToDest)">
<ItemGroup>
<_AoPubAppOfflineSourceProviderSetting Include="contentPath">
<Path>$(MSBuildProjectDirectory)\app_offline-template.htm</Path>
<EncryptPassword>$(DeployEncryptKey)</EncryptPassword>
<WebServerAppHostConfigDirectory>$(_MSDeploySourceWebServerAppHostConfigDirectory)</WebServerAppHostConfigDirectory>
<WebServerManifest>$(_MSDeploySourceWebServerManifest)</WebServerManifest>
<WebServerDirectory>$(_MSDeploySourceWebServerDirectory)</WebServerDirectory>
</_AoPubAppOfflineSourceProviderSetting>
<_AoPubAppOfflineDestProviderSetting Include="contentPath">
<Path>"$(DeployIisAppPath)/app_offline.htm"</Path>
<ComputerName>$(_PublishMsDeployServiceUrl)</ComputerName>
<UserName>$(UserName)</UserName>
<Password>$(Password)</Password>
<EncryptPassword>$(DeployEncryptKey)</EncryptPassword>
<IncludeAcls>False</IncludeAcls>
<AuthType>$(AuthType)</AuthType>
<WebServerAppHostConfigDirectory>$(_MSDeployDestinationWebServerAppHostConfigDirectory)</WebServerAppHostConfigDirectory>
<WebServerManifest>$(_MSDeployDestinationWebServerManifest)</WebServerManifest>
<WebServerDirectory>$(_MSDeployDestinationWebServerDirectory)</WebServerDirectory>
</_AoPubAppOfflineDestProviderSetting>
</ItemGroup>
<MSdeploy
MSDeployVersionsToTry="$(_MSDeployVersionsToTry)"
Verb="sync"
Source="@(_AoPubAppOfflineSourceProviderSetting)"
Destination="@(_AoPubAppOfflineDestProviderSetting)"
EnableRule="DoNotDeleteRule"
AllowUntrusted="$(AllowUntrustedCertificate)"
RetryAttempts="$(RetryAttemptsForDeployment)"
SimpleSetParameterItems="@(_AoArchivePublishSetParam)"
ExePath="$(MSDeployPath)" />
</Target>
<!--***********************************************************************
Make sure app_offline-template.htm gets published as app_offline.htm
***************************************************************************-->
<!-- We need to create a replace rule for app_offline-template.htm->app_offline.htm for when the app get's published -->
<ItemGroup>
<!-- Make sure not to include this file if a package is being created, so condition this on publishing -->
<FilesForPackagingFromProject Include="app_offline-template.htm" Condition=" '$(DeployTarget)'=='MSDeployPublish' ">
<DestinationRelativePath>app_offline.htm</DestinationRelativePath>
</FilesForPackagingFromProject>
<!-- This will prevent app_offline-template.htm from being published -->
<MsDeploySkipRules Include="SkipAppOfflineTemplate">
<ObjectName>filePath</ObjectName>
<AbsolutePath>app_offline-template.htm</AbsolutePath>
</MsDeploySkipRules>
</ItemGroup>
<!--***********************************************************************
When publish is completed we need to delete the app_offline.htm
***************************************************************************-->
<Target Name="DeleteAppOffline" AfterTargets="MSDeployPublish">
<!--
%msdeploy%
-verb:delete
-dest:contentPath="{IIS-Path}/app_offline.htm",computerName="...",username="...",password="..."
-->
<Message Text="************************************************************************" />
<Message Text="Calling MSDeploy to delete the app_offline.htm file" Importance="high" />
<Message Text="************************************************************************" />
<ItemGroup>
<_AoDeleteAppOfflineDestProviderSetting Include="contentPath">
<Path>$(DeployIisAppPath)/app_offline.htm</Path>
<ComputerName>$(_PublishMsDeployServiceUrl)</ComputerName>
<UserName>$(UserName)</UserName>
<Password>$(Password)</Password>
<EncryptPassword>$(DeployEncryptKey)</EncryptPassword>
<AuthType>$(AuthType)</AuthType>
<WebServerAppHostConfigDirectory>$(_MSDeployDestinationWebServerAppHostConfigDirectory)</WebServerAppHostConfigDirectory>
<WebServerManifest>$(_MSDeployDestinationWebServerManifest)</WebServerManifest>
<WebServerDirectory>$(_MSDeployDestinationWebServerDirectory)</WebServerDirectory>
</_AoDeleteAppOfflineDestProviderSetting>
</ItemGroup>
<!--
We cannot use the MSDeploy/VSMSDeploy tasks for delete so we have to call msdeploy.exe directly.
When they support delete we can just pass in @(_AoDeleteAppOfflineDestProviderSetting) as the dest
-->
<PropertyGroup>
<_Cmd>"$(MSDeployExe)" -verb:delete -dest:contentPath="%(_AoDeleteAppOfflineDestProviderSetting.Path)"</_Cmd>
<_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.ComputerName)' != '' ">$(_Cmd),computerName="%(_AoDeleteAppOfflineDestProviderSetting.ComputerName)"</_Cmd>
<_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.UserName)' != '' ">$(_Cmd),username="%(_AoDeleteAppOfflineDestProviderSetting.UserName)"</_Cmd>
<_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.Password)' != ''">$(_Cmd),password=$(Password)</_Cmd>
<_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.AuthType)' != ''">$(_Cmd),authType="%(_AoDeleteAppOfflineDestProviderSetting.AuthType)"</_Cmd>
</PropertyGroup>
<Exec Command="$(_Cmd)"/>
</Target>
</Project>
1 发布 app_offline.htm
#1 的实现包含在目标 PublishAppOfflineToDest 中。我们需要执行的 msdeploy.exe 命令是。
msdeploy.exe
-source:contentPath='C:\Data\Personal\My Repo\sayed-samples\AppOfflineDemo01\AppOfflineDemo01\app_offline-template.htm'
-dest:contentPath='"Default Web Site/AppOfflineDemo/app_offline.htm"',UserName='sayedha',Password='password-here',ComputerName='computername-here',IncludeAcls='False',AuthType='NTLM' -verb:sync -enableRule:DoNotDeleteRule
为了做到这一点,我将利用 MSDeploy 任务。在 PublishAppOfflineToDest 目标内部,您可以看到如何通过为源和目标创建项目来完成此操作。
2 发布应用,并确保 app_offline.htm 包含在要发布的负载中
这部分由片段完成
<!--***********************************************************************
Make sure app_offline-template.htm gets published as app_offline.htm
***************************************************************************-->
<!-- We need to create a replace rule for app_offline-template.htm->app_offline.htm for when the app get's published -->
<ItemGroup>
<!-- Make sure not to include this file if a package is being created, so condition this on publishing -->
<FilesForPackagingFromProject Include="app_offline-template.htm" Condition=" '$(DeployTarget)'=='MSDeployPublish' ">
<DestinationRelativePath>app_offline.htm</DestinationRelativePath>
</FilesForPackagingFromProject>
<!-- This will prevent app_offline-template.htm from being published -->
<MsDeploySkipRules Include="SkipAppOfflineTemplate">
<ObjectName>filePath</ObjectName>
<AbsolutePath>app_offline-template.htm</AbsolutePath>
</MsDeploySkipRules>
</ItemGroup>
此处 FilesForPackagingFromProject 的项目值会将您的 app_offline-template.htm 转换为将处理发布的文件夹中的 app_offline.htm。还有一个条件,它只发生在发布期间而不是打包期间。我们不希望 app_offline-template.htm 出现在包中(但如果它出现也不是世界末日)。
MsDeploySkiprules 的元素将确保 app_offline-template.htm 本身不会被发布。这可能不是必需的,但应该不会造成伤害。
3 删除 app_offline.htm
现在我们的应用已经发布,我们需要从目标网络应用中删除 app_offline.htm 文件。 msdeploy.exe 命令是:
%msdeploy%
-动词:删除
-dest:contentPath="{IIS-Path}/app_offline.htm",computerName="...",username="...",password="..."
这是在 DeleteAppOffline 目标内部实现的。这个目标将在发布后自动执行,因为我已经包含属性 AfterTargets=”MSDeployPublish”。在该目标中,您可以看到我正在直接构建 msdeploy.exe 命令,看起来 MSDeploy 任务不支持删除动词。
如果您尝试此操作,请在遇到任何问题时告诉我。我正在考虑从中创建一个 Nuget 包,以便您可以安装该包。这需要一些工作,所以如果您对此感兴趣,请告诉我。
资源
- The latest version of my AppOffline wpp.targets file
- ScottGu’s blog on app_offline.htm