本文3.0版本文章
更新
很多小伙伴在用 IIS 發布的時候,總是會有一些問題,文章下邊 #autoid-6-0-0 我也簡單的動圖展示了,如何 publish 到 IIS 的過程,如果你能看懂,卻發現自己的項目有問題的話,可以直接down 我 published 好的項目,地址:https://github.com/anjoy8/Blog.Data.Share/blob/master/netcoreapp2.2.rar ,下載解壓好后,先用 dotnet Blog.Core.dll 試試,肯定可以,是5000的端口,然后再發布到你自己的 IIS 代理服務器中,看看是否可以:
1、如果我的正常,你的還有問題,證明你的項目有問題,多半是缺少文件;
2、如果我的項目都異常,那就是你服務器的環境有問題,大概率是運行時安裝失敗;
3、如果看不懂,我另外寫了要給最新最全的部署文章,這篇看不懂,可以再看看這個 :《最全的部署方案 & 最豐富的錯誤分析》
代碼已上傳Github+Gitee,文末有地址
番外:時間真快,今天終於到了系統打包的日子,雖然項目還是有很多問題,雖然后邊還有很多的內容要說要學,但是想着初級基本的.Net Core 用到的基本至少就這么多了(接口文檔,項目框架,持久化ORM,依賴注入,AOP,分布式緩存,CORS跨域等等),中高級的,比如在Linux高級發布,Nginx代理,微服務,Dockers等等,這個在以后的更新中會慢慢提到,不然的話,Vue就一直說不到了 [哭笑哈哈],其實我還有很多要總結的,比如 Power BI系列(沒用過的點擊看看),比如C#7.0系列等文章,都在慢慢醞釀中,希望能堅持下來,不過這兩個系列目前還不會寫到,如果有需要用或學微軟PB的,可以加QQ群聯系我,我在微軟項目中已經用到了。還是打算從下周一開始轉戰Vue的文章,當然后端也會一直穿插着,這里要說下,我們的QQ群已經有一些小伙伴了,每天可以一起交流心得和問題,感覺還是很不錯的,如果你有什么問題,或者其他技術上的需要討論,咱們的群是可以試試喲,我和其他小伙伴會一直在線給大家解答(咋感覺像一個廣告哈哈,大家隨意哈)。
正傳:好啦,書接上文,昨天說到了《十二 || 三種跨域方式比較,DTOs(數據傳輸對象)初探》,因為下午時間的問題,只是講解了四種跨域方法,沒有講解完DTO,其實這個東西很簡單,說白了,就是把兩個實體類進行轉換,不用人工手動去一一賦值,今天呢,就簡單說下常見DTO框架AutoMapper的使用,然后做一個打包處理,發布到我的windows服務器里,今天剛剛買了一個Ubuntu Linux服務器,因為如果開發.Net Core,一定會接觸到Linux服務器,等各種,因為它跨域了,就是醬紫。但是還沒有配置好,所以會在下邊留下位置,慢慢補充在Ubuntu部署的講解。
零、今天完成右下角的深藍色部分

一、在項目中使用添加一個案例使用AutoMapper
1、普通的模型映射
在接口 IBlogArticleServices.cs和 類BlogArticleServices.cs中,添加GetBlogDetails()方法,返回類型是BlogViewModels
請看這兩個類
/// <summary> /// 博客文章實體類 /// </summary> public class BlogArticle { /// <summary> /// /// </summary> public int bID { get; set; } /// <summary> /// 創建人 /// </summary> public string bsubmitter { get; set; } /// <summary> /// 博客標題 /// </summary> public string btitle { get; set; } /// <summary> /// 類別 /// </summary> public string bcategory { get; set; } /// <summary> /// 內容 /// </summary> public string bcontent { get; set; } /// <summary> /// 訪問量 /// </summary> public int btraffic { get; set; } /// <summary> /// 評論數量 /// </summary> public int bcommentNum { get; set; } /// <summary> /// 修改時間 /// </summary> public DateTime bUpdateTime { get; set; } /// <summary> /// 創建時間 /// </summary> public System.DateTime bCreateTime { get; set; } /// <summary> /// 備注 /// </summary> public string bRemark { get; set; } } ------------------------------------------------- /// <summary> /// 博客信息展示類 /// </summary> public class BlogViewModels { /// <summary> /// /// </summary> public int bID { get; set; } /// <summary>/// 創建人 /// </summary> public string bsubmitter { get; set; } /// <summary>/// 博客標題 /// </summary> public string btitle { get; set; } /// <summary>/// 摘要 /// </summary> public string digest { get; set; } /// <summary> /// 上一篇 /// </summary> public string previous { get; set; } /// <summary> /// 上一篇id /// </summary> public int previousID { get; set; } /// <summary> /// 下一篇 /// </summary> public string next { get; set; } /// <summary> /// 下一篇id /// </summary> public int nextID { get; set; } /// <summary>/// 類別 /// </summary> public string bcategory { get; set; } /// <summary>/// 內容 /// </summary> public string bcontent { get; set; } /// <summary> /// 訪問量 /// </summary> public int btraffic { get; set; } /// <summary> /// 評論數量 /// </summary> public int bcommentNum { get; set; } /// <summary>/// 修改時間 /// </summary> public DateTime bUpdateTime { get; set; } /// <summary> /// 創建時間 /// </summary> public System.DateTime bCreateTime { get; set; } /// <summary>/// 備注 /// </summary> public string bRemark { get; set; } }
兩個實體類字段還基本可以,不是很多,但是我曾經開發一個旅游網站的系統,有一個表字段都高達30多個,當然還有更多的,額,如果我們一個個賦值是這樣的
BlogViewModels models = new BlogViewModels() { bsubmitter=blogArticle.bsubmitter, btitle = blogArticle.btitle, bcategory = blogArticle.bcategory, bcontent = blogArticle.bcontent, btraffic = blogArticle.btraffic, bcommentNum = blogArticle.bcommentNum, bUpdateTime = blogArticle.bUpdateTime, bCreateTime = blogArticle.bCreateTime, bRemark = blogArticle.bRemark, };
所以這個方法的全部代碼是:
接口層也要添加:
public interface IBlogArticleServices :IBaseServices<BlogArticle> { Task<List<BlogArticle>> getBlogs(); Task<BlogViewModels> getBlogDetails(int id); }
/// <summary> /// 獲取視圖博客詳情信息(一般版本) /// </summary> /// <param name="id"></param> /// <returns></returns> public async Task<BlogViewModels> getBlogDetails(int id) { var bloglist = await dal.Query(a => a.bID > 0, a => a.bID); var blogArticle = (await dal.Query(a => a.bID == id)).FirstOrDefault(); BlogViewModels models = null; if (blogArticle != null) { BlogArticle prevblog; BlogArticle nextblog; int blogIndex = bloglist.FindIndex(item => item.bID == id); if (blogIndex >= 0) { try { // 上一篇 prevblog = blogIndex > 0 ? (((BlogArticle)(bloglist[blogIndex - 1]))) : null; // 下一篇 nextblog = blogIndex + 1 < bloglist.Count() ? (BlogArticle)(bloglist[blogIndex + 1]) : null; BlogViewModels models = new BlogViewModels() { bsubmitter = blogArticle.bsubmitter, btitle = blogArticle.btitle, bcategory = blogArticle.bcategory, bcontent = blogArticle.bcontent, btraffic = blogArticle.btraffic, bcommentNum = blogArticle.bcommentNum, bUpdateTime = blogArticle.bUpdateTime, bCreateTime = blogArticle.bCreateTime, bRemark = blogArticle.bRemark, }; if (nextblog != null) { models.next = nextblog.btitle; models.nextID = nextblog.bID; } if (prevblog != null) { models.previous = prevblog.btitle; models.previousID = prevblog.bID; } } catch (Exception) { } } blogArticle.btraffic += 1; await dal.Update(blogArticle, new List<string> { "btraffic" }); } return models; }
想了想這才是一個方法,一般的系統都會有少則幾十,多則上百個這樣的方法,這還不算,大家肯定遇到過一個情況,如果有一天要在頁面多顯示一個字段,噗!不是吧,首先要存在數據庫,然后在該實體類就應該多一個,然后再在每一個賦值的地方增加一個,而且也沒有更好的辦法不是,一不小心就少了一個,然后被產品測試說咱們不細心,心塞喲,別慌!神器來了,一招搞定。
2、先來引入DTO講解,以及它的原理
在學習EF的時候我們知道了ORM(Object Relational Mapping)映射,是一種對象關系的映射,對象-關系映射(ORM)系統一般以中間件的形式存在,主要實現程序對象到關系數據庫數據的映射。
而Automapper是一種實體轉換關系的模型,AutoMapper是一個.NET的對象映射工具。主要作用是進行領域對象與模型(DTO)之間的轉換、數據庫查詢結果映射至實體對象。
下邊的是基本原理,大家喵一眼就行:
Ø 什么是DTO?
數據傳輸對象(DTO)(DataTransfer Object),是一種設計模式之間傳輸數據的軟件應用系統。數據傳輸目標往往是數據訪問對象從而從數據庫中檢索數據。數據傳輸對象與數據交互對象或數據訪問對象之間的差異是一個以不具有任何行為除了存儲和檢索的數據(訪問和存取器)。Ø 為什么用?
它的目的只是為了對領域對象進行數據封裝,實現層與層之間的數據傳遞。為何不能直接將領域對象用於數據傳遞?因為領域對象更注重領域,而DTO更注重數據。不僅如此,由於“富領域模型”的特點,這樣做會直接將領域對象的行為暴露給表現層。需要了解的是,數據傳輸對象DTO本身並不是業務對象。數據傳輸對象是根據UI的需求進行設計的,而不是根據領域對象進行設計的。比如,Customer領域對象可能會包含一些諸如FirstName, LastName, Email, Address等信息。但如果UI上不打算顯示Address的信息,那么CustomerDTO中也無需包含這個 Address的數據”。
Ø 什么是領域對象?
領域模型就是面向對象的,面向對象的一個很重要的點就是:“把事情交給最適合的類去做”,即:“你得在一個個領域類之間跳轉,才能找出他們如何交互”。在我們的系統中Model(EF中的實體)就是領域模型對象。領域對象主要是面對業務的,我們是通過業務來定義Model的。
以上的這些大家簡單看看原理即可,意思大家肯定都懂,下邊開始講解如何使用
3、引入 AutoMapper 的相關包
在Blog.Core.Services項目中引用Nuget包,AutoMapper 和 AutoMapper.Extensions.Microsoft.DependencyInjection
AutoMapper.Extensions.Microsoft.DependencyInjection,這個是用來配合依賴注入的,看名字也能看的出來吧,大家回憶下,整個項目中,都是使用的依賴注入,所以盡量不要用new 來實例化,導致層耦合。
4、添加映射文件 CustomProfile.cs
基於上邊原理,在接口層Blog.Core 中,添加文件夾AutoMapper,然后添加映射配置文件 CustomProfile.cs,用來匹配所有的映射對象關系
public class CustomProfile : Profile { /// <summary> /// 配置構造函數,用來創建關系映射 /// </summary> public CustomProfile() { CreateMap<BlogArticle, BlogViewModels>(); } }
下邊是來自熱心網友@菜工的解惑:
Profile不知有什么用,通過百度了解才了解是services.AddAutoMapper是會自動找到所有繼承了Profile的類然后進行配置,
而且我的這個配置文件是在api層的,如果Profile配置類放在別的層(比如Service層),
如果沒解耦的話,可以services.AddAutoMapper(),參數留空,AutoMapper會從所有引用的程序集里找繼承Profile的類,如果解耦了,就得services.AddAutoMapper(Assembly.Load("Blog.Core.Service"))。
大家看下F12這個CreateMap方法:
public IMappingExpression<TSource, TDestination> CreateMap<TSource, TDestination>();
第一個參數是原對象,第二個是目的對象,所以,要想好,是哪個方向轉哪個,當然可以都寫上,比如
CreateMap<BlogArticle, BlogViewModels>();
CreateMap<BlogViewModels, BlogArticle>();
//如果不想一個一個的配置,可以用接口的形式,批量導入 //這是一個思路,我沒有具體去寫,自行研究下,留個坑吧 //public interface IMapperTo<TDestination>{} //然后同樣來個Profile集中處理這些interface
/// <summary> /// 根據IMapperTo<>接口 自動初始化AutoMapper /// </summary> public class AutoMapperProfile : Profile { public override string ProfileName { get { return "AutoForIMapperTo"; } } protected override void Configure() { base.Configure(); typeof(SaveBuyerDemandRequest).Assembly.GetTypes()//SaveBuyerDemandRequest是TSource同屬的Assembly底下的任意類,要包含多個Aeembly的話自己擴展咯 .Where(i => i.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IMapperTo<>))) .ToList().ForEach(item => { item.GetInterfaces() .Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IMapperTo<>)) .ToList()//這里可以支持多個IMapperTo .ForEach(i => { var t2 = i.GetGenericArguments()[0]; Mapper.CreateMap(item, t2); Mapper.CreateMap(t2, item); }); }); } } //Class For Example public class SaveBuyerDemandRequest : IMapperTo<BuyerDemandEntity> { }
5、使用AutoMapper實現模型映射,並注入
老規矩,還是在Startup中,注入服務
services.AddAutoMapper(typeof(Startup));//這是AutoMapper的2.0新特性
修改上邊服務層BlogArticleServices.cs 中getBlogDetails 方法中的賦值,改用AutoMapper,並用構造函數注入
最終的代碼是:
// 依賴注入 IBlogArticleRepository dal; IMapper IMapper; public BlogArticleServices(IBlogArticleRepository dal, IMapper IMapper) { this.dal = dal; base.baseDal = dal; this.IMapper = IMapper; } /// <summary> /// 獲取視圖博客詳情信息 /// </summary> /// <param name="id"></param> /// <returns></returns> public async Task<BlogViewModels> getBlogDetails(int id) { var bloglist = await dal.Query(a => a.bID > 0, a => a.bID); var blogArticle = (await dal.Query(a => a.bID == id)).FirstOrDefault(); BlogViewModels models = null; if (blogArticle != null) { BlogArticle prevblog; BlogArticle nextblog; int blogIndex = bloglist.FindIndex(item => item.bID == id); if (blogIndex >= 0) { try { // 上一篇 prevblog = blogIndex > 0 ? (((BlogArticle)(bloglist[blogIndex - 1]))) : null; // 下一篇 nextblog = blogIndex + 1 < bloglist.Count() ? (BlogArticle)(bloglist[blogIndex + 1]) : null; // 注意就是這里,mapper models = IMapper.Map<BlogViewModels>(blogArticle); if (nextblog != null) { models.next = nextblog.btitle; models.nextID = nextblog.bID; } if (prevblog != null) { models.previous = prevblog.btitle; models.previousID = prevblog.bID; } } catch (Exception) { } } blogArticle.btraffic += 1; await dal.Update(blogArticle, new List<string> { "btraffic" }); } return models; }
修改BlogController.cs中的 Get(int id)方法,運行項目,斷點調試,發現已經成功了,是不是很方便,你也可以反過來試一試
[HttpGet("{id}", Name = "Get")] public async Task<object> Get(int id) { var model = await _blogArticleServices.getBlogDetails(id);//調用該方法,這里 _blogArticleServices 是依賴注入的實例,不是類 var data = new { success = true, data = model }; return data; }

好啦,到目前為止,咱們已經注入了這些服務了:

6、復雜深拷貝映射
有的小伙伴問,你這個這個簡單,都是相同字段的,那當然很方便啦,要是一個復雜的,比如屬性名字不一樣的,或者說有子類等嵌入型的咋辦?放心,一樣是可以的,請看
1、屬性名稱不一樣
CreateMap<Student, StudentViewModel>() .ForMember(d => d.CountyName, o => o.MapFrom(s => s.County)) .ForMember(d => d.ProvinceName, o => o.MapFrom(s => s.Province)) ;
2、如果是還有子類的復雜類型
CreateMap<Student, StudentViewModel>() .ForMember(d => d.County, o => o.MapFrom(s => s.Address.County)) .ForMember(d => d.Province, o => o.MapFrom(s => s.Address.Province)) .ForMember(d => d.City, o => o.MapFrom(s => s.Address.City)) .ForMember(d => d.Street, o => o.MapFrom(s => s.Address.Street)) ; public class Student : Entity { public string Name { get; private set; } public string Email { get; private set; } public string Phone { get; private set; } public DateTime BirthDate { get; private set; } public Address Address { get; private set; } } public class StudentViewModel { public Guid Id { get; set; } public string Name { get; set; } public string Email { get; set; } public DateTime BirthDate { get; set; } public string Phone { get; set; } public string Province { get; set; } public string City { get; set; } public string County { get; set; } public string Street { get; set; } }
二、Blog.Core項目打包發布在IIS
1、項目打包發布
在項目Blog.Core中,右鍵,發布,選擇文件,相信大家都會,不會的可以聯系我


注意1: 這里有一個坑,還記得我們用swagger中使用的兩個xml文件,記得是兩個文件,編譯的時候有,但是.net core官方限制了在發布的時候包含xml文件,所以我們需要處理下
在發布之前,我們手動在項目工程文件 blog.core.csproj中,增加
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
或者,右鍵xml文檔,屬性,始終復制,這些都是 net 的基本技能,有問題百度下就知道了。
----------------------------------------------------------------------
當然我們還可以基於CLI的Publish命令進行發布,只需切換到Light.API根目錄下,輸入以下命令即可:
dotnet publish --framework netcoreapp1.1 --output "E:\Publish" --configuration Release
framework表示目標框架,output表示要發布到的目錄文件夾,configuration表示配置文件,等同於和上面我們通過管理器來發布的操作
具體的大家可以自行實驗
注意2:如果你想發布到其他文件夾,可以使用生成命令:
是因為我在 api 層的項目屬性中,配置了生成命令:
Copy "$(ProjectDir)bin\Debug\netcoreapp2.2\" "$(SolutionDir)Blog.Core\bin\Debug\"
提示:
我們發布項目的時候,會生成一個web.config文件,這個web.config文件是為了IIS而作用的,如果用基於CLI的dotnet命令啟動,則不需要這個config。
2、安裝運行時Runtime(只能運行.net core應用程序,不能開發)
比如服務器里,可以僅僅安裝運行時即可,如果不安裝,你可能會遇到這個錯誤:

下載地址:https://www.microsoft.com/net/download/windows

在CMD命令窗口下,輸入 dotnet 和 dotnet --list-runtimes 查看

注意:如果你是本地開發,還要安裝SDK,下文會提到,如果只想服務器中運行,只安裝上邊的運行時即可,(這里的運行是能dotnet xxx.dll跑起來,而不是命令行dotnet run啟動)
比如你安裝后,輸入 dotnet --version 會報錯,下邊這個錯誤需要安裝 SDK,不用理會,只要保證 dotnet 的命令 能正常就行

怎么保證安裝好了呢,直接在服務器中的項目目錄下,執行 dotnet xxxx.dll,通過kestrel服務器運行,如果正常則安裝成功,可以繼續配置iis,如果報錯,就是沒有安裝成功。

至於為啥沒有安裝成功,我知道的三點:
1、沒有重啟
2、有的服務器是x64的,但是需要安裝x86的
3、執行命令,dotnet --list-runtimes 沒有找到相應的版本
3、安裝SDK(windows服務器不用安裝)
https://dotnet.microsoft.com/download/visual-studio-sdks?utm_source=getdotnetsdk&utm_medium=referral
https://www.microsoft.com/net/learn/dotnet/hello-world-tutorial
4、安裝AspNetCoreModule托管模塊(已安裝則跳過),
下載地址:點擊我下載

5、應用池配置為無托管代碼
(網上解釋:ASP.NET Core不再是由IIS工作進程(w3wp.exe)托管,而是使用自托管Web服務器(Kestrel)運行,IIS則是作為反向代理的角色轉發請求到Kestrel不同端口的ASP.NET Core程序中,隨后就將接收到的請求推送至中間件管道中去,處理完你的請求和相關業務邏輯之后再將HTTP響應數據重新回寫到IIS中,最終轉達到不同的客戶端(瀏覽器,APP,客戶端等)。而配置文件和過程都會由些許調整,中間最重要的角色便是AspNetCoreModule,它是其中一個的IIS模塊,請求進入到IIS之后便立即由它轉發,並迅速重定向到ASP.NET Core項目中,所以這時候我們無需設置應用程序池來托管我們的代碼,它只負責轉發請求而已)

老張:如果需要讀寫根目錄權限,要更改應用池 ApplicationPoolIdentity
6、可以打開錯誤日志
在發布的時候,會有一個web.config出現,通過修改web.config 啟用錯誤日志查看詳細錯誤信息
將stdoutLogEnabled的修改為 true,並在應用程序根目錄添加 logs 文件夾
一定要手動添加logs文件,不然會不出現
但是這個文件名應該不能被修改:

7、只要本地能通過,常見的錯誤就是生成的文件不全導致的,大家可以自行看看,如果有問題,也可以大家一起解決
比如錯誤
1、缺少一個補丁

其中一個問題是少一個補丁,發現需要打個補丁(Windows6.1-KB2533623-x64.msu),下載地址:點我點我。
8、在IIS中啟動項目,或者直接輸入服務器IP地址,加端口調試
注意:這里有一個小問題,因為發布以后,默認啟動頁是在開發環境中重定向到了swagger,但是在服務器部署以后,不能跳轉,大家打開后會這樣,404找不到,不要怕,
只需要在后邊加上Swagger就行了

9、配置域名
當前端口配置域名的時候,需要在IIS的應用程序池中,修改“加載用戶配置文件”為 True

三、項目在Liunx Ubuntu中部署(簡單版,慢慢完善)
1、在騰訊雲購買Ubuntu服務器后,登陸,然后進入命令頁面

2、部署Linux系統中的微軟環境
繼續執行下面的命令
Register the trusted Microsoft signature key:
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
繼續
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
根據系統版本,執行下面的命令
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
好了,環境部署完畢,下面我們安裝 SDK
3、部署.Ne Core 環境
sudo apt-get install apt-transport-https sudo apt-get update sudo apt-get install dotnet-sdk-2.1.4
安裝成功后,輸入命令 dotnet

證明安裝成功啦
4、安裝代碼上傳工具,Fillzila或者winSCP都可以,(我用的是winSCP)
軟件下好打開后界面是這樣的,我們需要填的就是主機名(你服務器的公網IP)、用戶名(服務器的用戶名)、密碼(你買服務器時設置的密碼),那個文件協議就是SFTP,不用改變

5、登陸進去默認是 /Home/ubuntu 文件夾,我們都在這里操作

6、下面我們在服務器新建一個控制台項目測試一下
dotnet new console -o myApp

然后就在winSCP發現多了一個項目

7、然后運行我們剛剛創建的項目
cd myApp
dotnet run

代碼一起正常!
8、把我們的項目發布上去,注意這里不是咱們發布的版本!不是發布的版本!
因為我們本地發布的是windows版本的,如果把publish打包版本發布上去,會報錯,各種錯
所以應該是把整個解決方法提交上去,當然git就別提交了

然后呢,進入到我們要發布的接口層項目
cd Blog.Core,然后再cd Blog.Core
最后執行 dotnet run 即可

四、發布到Ubuntu
參考文章 @發布 ASP.NET Core 2.x 應用到 Ubuntu
1、安裝.NET Core
首先需要安裝.NET Core Runtime: https://www.microsoft.com/net/download

點擊之后,根據您的Linux發行版不同,選擇相應的操作步驟:


最后執行dotnet --info驗證安裝是否成功:

2、安裝Nginx
另外還需要安裝Nginx,直接查看官網文檔吧:https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-2.1&tabs=aspnetcore2x#install-nginx
安裝好后,訪問這個頁面:http://你的ip地址/index.nginx-debian.html,如果看到如下效果說明安裝成功:

3、在服務器構建源碼並發布
然后就是發布程序了,發布有兩種辦法:
- 在開發機上執行dotnet publish然后把發布的文件復制到服務器上
- 或者直接在服務器上使用源碼構建並發布,我一般是這樣做的。
由於我是直接在服務器上構建發布,所以我需要安裝.NET Core SDK:https://www.microsoft.com/net/learn/get-started-with-dotnet-tutorial

然后就可以使用發布命令了:dotnet publish --configuration Release。
發布好的文件在bin/Release/netcoreapp*.*/publish下面。
再把publish下的所有文件復制到我的目標文件夾即可:

在我的目標目錄下,有這些文件:

如果執行 dotnet test.dll,這個程序就會在localhost:5000運行:

4、配置Nginx
然后我們再回來配置Nginx,進入/etc/nginx/sites-available,里面有一個Default文件,把它改個名,然后我們再建立一個新的Default文件:

保存后執行sudo nginx -t檢驗這個配置文件。
然后再執行 nginx -s reload 來重啟nginx。
隨后需要再把發布后的程序運行一下:dotnet test.dll:

在我使用網址訪問80端口的時候,會自動跳轉到5001端口,導致連接失敗:

這是因為項目里默認使用了HTTPS Redirection。因為我沒有證書,所以為了演示,我把HTTPS Redirection相關的代碼注釋掉,再發布:

重復上述步驟之后,通過網址的80端口,就可以正常訪問了:

5、NGINX配置證書和HTTPS
配置HTTPS和證書相關的內容直接去看官方文檔:https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-2.1&tabs=aspnetcore2x#configure-ssl
添加proxy.conf和編輯nginx.conf后重啟nginx即可。
按照操作,運行后如果不能使用https正常訪問網站,那么有可能是無法綁定443端口導致的。
查看nginx錯誤日志:/var/log/nginx/error.log,如果出現下面的錯誤:

可以執行下列命令來解決:
sudo fuser -k 443/tcp
service nginx restart
然后再次訪問https網址:

這樣就可以正常訪問https的網址了。
五、NetCore 部署到 WINDOWS服務
微軟有提供 如何在windows服務托管asp.net core ,不過步驟比較麻煩,還需要改源碼,網上找到一種方法 使用NSSM把.Net Core部署至windows服務
簡單說一下步驟
1. 下載nssm:http://www.nssm.cc/download
2. 運行cmd,定位到nssm.exe文件路徑,運行nssm install
3. 在彈出的窗口配置:
Path:dotnet所在的目錄,一般默認是在C:\Program Files\dotnet\dotnet.exe;
Startup directory:程序所在的目錄,就是最后程序dll所在的目錄;
Arguments:程序dll的名稱,一般是項目名加上.dll;
Service name:在此寫上服務的名稱即可。
最后點擊install service 完成windows服務安裝。
在windows服務找到對應服務名,啟動,然后根據launchSettings.json配置的端口訪問,即可調取接口。

六、結語
今天暫時就先寫到這里,我們學到了如何用AutoMapper來實現DTO數據對象映射,也學會了在windows下的IIS中發布項目,最后就是Linux系統中,搭建環境和運行.net core 。以后呢我還會講到Docker 容器,Nginx代理等等,大家拭目以待吧
七、CODE
https://github.com/anjoy8/Blog.Core
https://gitee.com/laozhangIsPhi/Blog.Core

