在.NET Core项目开发过程当中,为了实现代码复用,咱们将能够重复使用的部分拆分红一个个小的NuGet包。这些NuGet包能够在其余系统中复用,这样咱们只须要实现系统特定的代码,其他部分的就能够重用了,包括功能、文档等。使用过程当中,功能复用没有遇到任何问题,可是文档复用却遇到了问题。咱们使用SwashBuckle生成Swagger定义和Swagger UI。Swashbuckle须要XML文档,才能显示控制器和模型的文档说明。不幸的是,Swagger中不能正常显示NuGet包的模型文档说明。json
咱们能够用在项目文件.csproj
的PropertyGroup
中添加<GenerateDocumentationFile>true</GenerateDocumentationFile>
的方式,将XML文档添加到NuGet包中,而后将XML文档手动拷贝到项目里,做为内容输出。Swagger中就能正常显示NuGet包的模型文档说明了。可是这样作,若是NuGet包版本更新,就须要从新手动拷贝XML文档,感受不太优雅。是否有更优雅的方式呢?缓存
Google到几篇文章,方案大同小异,不知道可不可行。试试吧!期待能够成功!架构
实战
咱们涉及到两个项目:app
ICH.NetCore2.Test.WebApi
:ASP.NET Core WebApi 主项目ICH.Common
:可重用的公共组件NuGet包
咱们按照如下几个步骤分布实施 。测试
一、设置.csproj文件构建NuGet包时包含XML文档
ICH.Common
的项目文件.csproj
的PropertyGroup
中添加<GenerateDocumentationFile>true</GenerateDocumentationFile>
,做用是设置.csproj文件构建NuGet包时包含XML文档。ui
<PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup>
二、定义从NuGet包文件夹的根目录到XML文档所在位置的相对路径
ICH.NetCore2.Test.WebApi
的项目文件.csproj
的PackageReference
中添加<CopyToOutputDirectory>lib\netstandard2.0\*.xml</CopyToOutputDirectory>
,做用是定义从NuGet包文件夹的根目录到XML文档所在位置的相对路径。this
<ItemGroup> <PackageReference Include="ICH.Common" Version="$(ICHCoreFXVersion)"> <CopyToOutputDirectory>lib\netstandard2.0\*.xml</CopyToOutputDirectory> </PackageReference> </ItemGroup>
- 在.NET Core中,咱们的NuGet包位于:
%USERPROFILE%\.nuget\packages\{PackageName}\{PackageVersion}
- XML文档文件位于:
%USERPROFILE%\.nuget\packages\{PackageName}\{PackageVersion}\lib\netcoreapp2.1\{PackageName}.xml
三、设置构建后从NuGet包中复制XML文档
ICH.NetCore2.Test.WebApi
的项目文件.csproj
添加<Target Name="AfterTargetsBuild" AfterTargets="Build">
,做用是设置构建后从NuGet包中复制XML文档。url
<Target Name="AfterTargetsBuild" AfterTargets="Build"> <ItemGroup> <PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)\%(PackageReference.Identity)\%(PackageReference.Version)\%(PackageReference.CopyToOutputDirectory)" /> </ItemGroup> <Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(OutDir)" /> </Target>
四、设置发布后从NuGet包中复制XML文档
与上一步相似,ICH.NetCore2.Test.WebApi
的项目文件.csproj
添加<Target Name="AfterTargetsPublish" AfterTargets="Publish">
,做用是设置发布后从NuGet包中复制XML文档。spa
<Target Name="AfterTargetsPublish" AfterTargets="Publish"> <ItemGroup> <PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)\%(PackageReference.Identity)\%(PackageReference.Version)\%(PackageReference.CopyToOutputDirectory)" /> </ItemGroup> <Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(PublishDir)" /> </Target>
五、添加XML文档到Swagger定义中
ICH.NetCore2.Test.WebApi
的项目Startup
设置options.IncludeXmlComments(xmlFile, true)
,做用是添加XML文档到Swagger定义中。.net
public static void ConfigureSwagger(this IServiceCollection) { services.AddSwaggerGen(c => { string[] xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml"); foreach (var xmlFile in xmlFiles) options.IncludeXmlComments(xmlFile, true); }); }
六、测试
如今是见证奇迹的时候了。 首先本地构建一下试试,心情忐忑! 能够看到XML文档从NuGet包中复制到输出目录了,奈斯!
而后本地发布一下试试,心情依然忐忑! 能够看到XML文档从NuGet包中复制到输出目录了, 外瑞奈斯!
最后再看看Swagger效果,Swagger中也能正常显示NuGet包的模型文档说明。
七、遇坑
就这么简单?有一个声音告诉我,图样图森破! 咱们生产环境是采用DockerFile的CICD方式自动发布到K8S集群,发布以后,在生产环境Swagger中并不能正常显示NuGet包的模型文档说明。 Why?Why?Why?另外一个声音告诉我,不要慌,冷静思考!
咱们的Dockerfile
内容以下:
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . WORKDIR /src/src/ICH.NetCore2.Test.WebApi RUN dotnet publish ICH.NetCore2.Test.WebApi.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=build /app . RUN ls RUN rm -rf appsettings.Development.json RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' >/etc/timezone ENTRYPOINT ["dotnet", "ICH.NetCore2.Test.WebApi.dll"]
看了一下CICD日志,发现输出目录并无ICH.Common.xml
文件。 这就存在两种可能:
- 要么缓存的NuGet包中不存在XML文档
- 要么缓存的NuGet包中存在XML文档,主项目发布时却没有从NuGet包中复制XML文档。
咱们一个个来测试。先看看缓存的NuGet包中到底存不存在XML文档。参考本地NuGet包路径,在Dockerfile
中添加RUN ls /root/.nuget/packages/ich.common/3.0.0-ci.63367-beta/lib/netstandard2.0
,打印NuGet包中的文件。
注意:Windows和Mac/Linux的global‑packages位置不一致
具体能够参考微软官方文档
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . WORKDIR /src/src/ICH.NetCore2.Test.WebApi RUN dotnet publish ICH.NetCore2.Test.WebApi.csproj -c Release -o /app RUN ls /root/.nuget/packages/ich.common/3.0.0-ci.63367-beta/lib/netstandard2.0 #打印NuGet包中的文件 FROM base AS final WORKDIR /app COPY --from=build /app . RUN ls RUN rm -rf appsettings.Development.json RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' >/etc/timezone ENTRYPOINT ["dotnet", "ICH.NetCore2.Test.WebApi.dll"]
再试试! 惊,为何缓存的NuGet包中不存在XML文档呢?不该该啊,本地测试不是好好的吗?难道跟平台有关系?本地是Windows,生产环境是Docker。 Google到微软官方文档,关注
NuGet CLI environment variables
中有一个环境变量NUGET_XMLDOC_MODE
。
NUGET_XMLDOC_MODE
Determines how assemblies XML documentation file extraction should be handled. Supported modes are skip (do not extract XML documentation files), compress (store XML doc files as a zip archive) or none (default, treat XML doc files as regular files). 定义如何处理程序集XML文档文件提取, 无论怎么样,试试吧! 在Dockerfile
中添加ENV NUGET_XMLDOC_MODE none
,设置环境变量NUGET_XMLDOC_MODE
值为none
,也就是 NuGet将XML文档文件视为常规文件。
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . WORKDIR /src/src/ICH.NetCore2.Test.WebApi ENV NUGET_XMLDOC_MODE none # 设置环境变量NUGET_XMLDOC_MODE RUN dotnet publish ICH.NetCore2.Test.WebApi.csproj -c Release -o /app RUN ls /root/.nuget/packages/ich.common/3.0.0-ci.63367-beta/lib/netstandard2.0 #打印NuGet包中的文件 FROM base AS final WORKDIR /app COPY --from=build /app . RUN ls RUN rm -rf appsettings.Development.json RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' >/etc/timezone ENTRYPOINT ["dotnet", "ICH.NetCore2.Test.WebApi.dll"]
再试试! 奈斯,能够看到ICH.Common.xml
文件静静的躺在那! 再看看输出目录,竟然...竟然...没有
ICH.Common.xml
文件。
静静思考,会不会是第4步设置发布后从NuGet包中复制XML文档
的路径问题。
修改ICH.NetCore2.Test.WebApi
的项目文件.csproj
。
<Target Name="AfterTargetsPublish" AfterTargets="Publish"> <ItemGroup> <PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="/root/.nuget/packages/ich.common/3.0.0-ci.63367-beta/lib/netstandard2.0/*.xml" /> </ItemGroup> <Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(PublishDir)" /> </Target>
再试试!输出目录能够看到ICH.Common.xml
文件了。 果真是路径问题,也就是说
$(NugetPackageRoot)\%(PackageReference.Identity)\%(PackageReference.Version)
不等于/root/.nuget/packages/ich.common/3.0.0-ci.63367-beta
。 为何,难道是大小写的问题?
修改ICH.NetCore2.Test.WebApi
的项目文件.csproj
,将PackageReference.Identity
转为小写。
<Target Name="AfterTargetsPublish" AfterTargets="Publish"> <ItemGroup> <PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)\$([MSBuild]::Escape('%(PackageReference.Identity)').ToLower())\%(PackageReference.Version)\lib\%(PackageReference.CopyToOutputDirectory)\*.xml" /> </ItemGroup> <Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(PublishDir)" /> </Target>
再试试!输出目录能够看到ICH.Common.xml
文件了。 最后再看看Swagger效果,Swagger中也能正常显示NuGet包的模型文档说明。完美!
总结
填坑的过程是曲折的,收获是颇丰的,结局是圆满的。 最后贴出完整的ICH.NetCore2.Test.WebApi
的项目文件.csproj
和Dockerfile
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> <ItemGroup> <PackageReference Include="ICH.Common" Version="$(ICHCoreFXVersion)"> <CopyToOutputDirectory>lib\netstandard2.0\*.xml</CopyToOutputDirectory> </PackageReference> </ItemGroup> <Target Name="AfterTargetsBuild" AfterTargets="Build"> <ItemGroup> <PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)\$([MSBuild]::Escape('%(PackageReference.Identity)').ToLower())\%(PackageReference.Version)\%(PackageReference.CopyToOutputDirectory) /> </ItemGroup> <Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(OutDir)" /> </Target> <Target Name="AfterTargetsPublish" AfterTargets="Publish"> <ItemGroup> <PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)\$([MSBuild]::Escape('%(PackageReference.Identity)').ToLower())\%(PackageReference.Version)\%(PackageReference.CopyToOutputDirectory)" /> </ItemGroup> <Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(PublishDir)" /> </Target> </Project>
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . WORKDIR /src/src/ICH.NetCore2.Test.WebApi ENV NUGET_XMLDOC_MODE none # 设置环境变量NUGET_XMLDOC_MODE RUN dotnet publish ICH.NetCore2.Test.WebApi.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=build /app . RUN ls RUN rm -rf appsettings.Development.json RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' >/etc/timezone ENTRYPOINT ["dotnet", "ICH.NetCore2.Test.WebApi.dll"]
最后
感谢几篇文章的做者给与个人启发,同时也但愿这篇文章能够帮助你们解决相似的问题。
<div style="display: none;"> <span id="fulu-org">福禄ICH·架构组</span> <span id="fulu-author">福小皮</span> </div>