ASP.NET CORE 第七篇 DTOs 對象映射使用,項目部署Windows+Linux完整版

原文作者:老張的哲學(xué)

零、今天完成右下角的深藍(lán)色部分

image.png

一、在項目中使用添加一個案例使用AutoMapper

1、普通的模型映射
在接口 IBlogArticleServices.cs和 類BlogArticleServices.cs中,添加GetBlogDetails()方法,返回類型是BlogViewModels

請看這兩個類

/// <summary>
    /// 博客文章實體類
    /// </summary>
    public class BlogArticle
    {
        /// <summary>
        /// 
        /// </summary>
        public int bID { get; set; }
        /// <summary>
        /// 創(chuàng)建人
        /// </summary>
        public string bsubmitter { get; set; }

        /// <summary>
        /// 博客標(biāo)題
        /// </summary>
        public string btitle { get; set; }

        /// <summary>
        /// 類別
        /// </summary>
        public string bcategory { get; set; }

        /// <summary>
        /// 內(nèi)容
        /// </summary>
        public string bcontent { get; set; }

        /// <summary>
        /// 訪問量
        /// </summary>
        public int btraffic { get; set; }

        /// <summary>
        /// 評論數(shù)量
        /// </summary>
        public int bcommentNum { get; set; }

        /// <summary> 
        /// 修改時間
        /// </summary>
        public DateTime bUpdateTime { get; set; }

        /// <summary>
        /// 創(chuàng)建時間
        /// </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>/// 創(chuàng)建人
        /// </summary>
        public string bsubmitter { get; set; }

        /// <summary>/// 博客標(biāo)題
        /// </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>/// 內(nèi)容
        /// </summary>
        public string bcontent { get; set; }

        /// <summary>
        /// 訪問量
        /// </summary>
        public int btraffic { get; set; }

        /// <summary>
        /// 評論數(shù)量
        /// </summary>
        public int bcommentNum { get; set; }

        /// <summary>/// 修改時間
        /// </summary>
        public DateTime bUpdateTime { get; set; }

        /// <summary>
        /// 創(chuàng)建時間
        /// </summary>
        public System.DateTime bCreateTime { get; set; }
        /// <summary>/// 備注
        /// </summary>
        public string bRemark { get; set; }
    }

兩個實體類字段還基本可以,不是很多,但是我曾經(jīng)開發(fā)一個旅游網(wǎng)站的系統(tǒng),有一個表字段都高達(dá)30多個,當(dāng)然還有更多的,額,如果我們一個個賦值是這樣的

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,
            };    

所以這個方法的全部代碼是:
接口層也要添加:

using Blog.Core.IServices.Base;
using Blog.Core.Model;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.Core.IServices
{
    public interface IArticleServices : IBaseServices<Article>
    {
        Task<List<Article>> getBlogs();
        Task<ViewModels> getBlogDetails(int id);
    }
}
using Blog.Core.Common.Attribue;
using Blog.Core.IRepository;
using Blog.Core.IServices;
using Blog.Core.Model;
using Blog.Core.Services.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blog.Core.Services
{
    public class ArticleServices : BaseServices<Article>, IArticleServices
    {
        readonly IArticleRepository dal;

        public ArticleServices(IArticleRepository dal)
        {
            this.dal = dal;
            base.baseDal = dal;
        }

        /// <summary>
        /// 獲取視圖博客詳情信息(一般版本)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<ViewModels> 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();
            ViewModels models = null;
            if(blogArticle != null)
            {
                Article prevBlog;
                Article nextBlog;
                int blogIndex = blogList.FindIndex(item => item.bID == id);
                if(blogIndex >= 0)
                {
                    try
                    {
                        // 上一篇
                        prevBlog = blogIndex > 0 ? blogList[blogIndex - 1] : null;
                        // 下一篇
                        nextBlog = blogIndex + 1 < blogList.Count() ? blogList[blogIndex - 1] : null;

                        models = new ViewModels()
                        {
                            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;
        }

        /// <summary>
        /// 獲取博客列表
        /// </summary>
        /// <returns></returns>
        [Caching(AbsoluteExpiration = 20)] // 添加特性
        public async Task<List<Article>> getBlogs()
        {
            var blogList = await dal.Query(x => x.bID < 5);
            return blogList;
        }
    }
}

想了想這才是一個方法,一般的系統(tǒng)都會有少則幾十,多則上百個這樣的方法,這還不算,大家肯定遇到過一個情況,如果有一天要在頁面多顯示一個字段,噗!不是吧,首先要存在數(shù)據(jù)庫,然后在該實體類就應(yīng)該多一個,然后再在每一個賦值的地方增加一個,而且也沒有更好的辦法不是,一不小心就少了一個,然后被產(chǎn)品測試說咱們不細(xì)心,心塞喲,別慌!神器來了,一招搞定。

2、先來引入DTO講解,以及它的原理

在學(xué)習(xí)EF的時候我們知道了ORM(Object Relational Mapping)映射,是一種對象關(guān)系的映射,對象-關(guān)系映射(ORM)系統(tǒng)一般以中間件的形式存在,主要實現(xiàn)程序?qū)ο蟮疥P(guān)系數(shù)據(jù)庫數(shù)據(jù)的映射。

而Automapper是一種實體轉(zhuǎn)換關(guān)系的模型,AutoMapper是一個.NET的對象映射工具。主要作用是進(jìn)行領(lǐng)域?qū)ο笈c模型(DTO)之間的轉(zhuǎn)換、數(shù)據(jù)庫查詢結(jié)果映射至實體對象。

下邊的是基本原理,大家喵一眼就行:

? 什么是DTO?
  數(shù)據(jù)傳輸對象(DTO)(DataTransfer Object),是一種設(shè)計模式之間傳輸數(shù)據(jù)的軟件應(yīng)用系統(tǒng)。數(shù)據(jù)傳輸目標(biāo)往往是數(shù)據(jù)訪問對象從而從數(shù)據(jù)庫中檢索數(shù)據(jù)。數(shù)據(jù)傳輸對象與數(shù)據(jù)交互對象或數(shù)據(jù)訪問對象之間的差異是一個以不具有任何行為除了存儲和檢索的數(shù)據(jù)(訪問和存取器)。

? 為什么用?
  它的目的只是為了對領(lǐng)域?qū)ο筮M(jìn)行數(shù)據(jù)封裝,實現(xiàn)層與層之間的數(shù)據(jù)傳遞。為何不能直接將領(lǐng)域?qū)ο笥糜跀?shù)據(jù)傳遞?因為領(lǐng)域?qū)ο蟾⒅仡I(lǐng)域,而DTO更注重數(shù)據(jù)。不僅如此,由于“富領(lǐng)域模型”的特點,這樣做會直接將領(lǐng)域?qū)ο蟮男袨楸┞督o表現(xiàn)層。
  需要了解的是,數(shù)據(jù)傳輸對象DTO本身并不是業(yè)務(wù)對象。數(shù)據(jù)傳輸對象是根據(jù)UI的需求進(jìn)行設(shè)計的,而不是根據(jù)領(lǐng)域?qū)ο筮M(jìn)行設(shè)計的。比如,Customer領(lǐng)域?qū)ο罂赡軙恍┲T如FirstName, LastName, Email, Address等信息。但如果UI上不打算顯示Address的信息,那么CustomerDTO中也無需包含這個 Address的數(shù)據(jù)”。

? 什么是領(lǐng)域?qū)ο螅?br>   領(lǐng)域模型就是面向?qū)ο蟮?,面向?qū)ο蟮囊粋€很重要的點就是:“把事情交給最適合的類去做”,即:“你得在一個個領(lǐng)域類之間跳轉(zhuǎn),才能找出他們?nèi)绾谓换ァ薄T谖覀兊南到y(tǒng)中Model(EF中的實體)就是領(lǐng)域模型對象。領(lǐng)域?qū)ο笾饕敲鎸I(yè)務(wù)的,我們是通過業(yè)務(wù)來定義Model的。

以上的這些大家簡單看看原理即可,意思大家肯定都懂,下邊開始講解如何使用

3、引入 AutoMapper 的相關(guān)包

在Blog.Core.Services項目中引用Nuget包,AutoMapper 和 AutoMapper.Extensions.Microsoft.DependencyInjection

AutoMapper.Extensions.Microsoft.DependencyInjection,這個是用來配合依賴注入的,看名字也能看的出來吧,大家回憶下,整個項目中,都是使用的依賴注入,所以盡量不要用new 來實例化,導(dǎo)致層耦合。

4、添加映射文件 CustomProfile.cs

基于上邊原理,在接口層Blog.Core 中,添加文件夾AutoMapper,然后添加映射配置文件 CustomProfile.cs,用來匹配所有的映射對象關(guān)系

using AutoMapper;
using Blog.Core.Model;

namespace Blog.Core.API.AutoMapper
{
    /// <summary>
    /// 配置構(gòu)造函數(shù),用來創(chuàng)建關(guān)系映射
    /// </summary>
    public class CustomProfile : Profile
    {
        public CustomProfile()
        {
            CreateMap<Article, ViewModels>();
        }
    }
}

大家看下F12這個CreateMap方法:

public IMappingExpression<TSource, TDestination> CreateMap<TSource, TDestination>();

第一個參數(shù)是原對象,第二個是目的對象,所以,要想好,是哪個方向轉(zhuǎn)哪個,當(dāng)然可以都寫上,比如

CreateMap<BlogArticle, BlogViewModels>();
CreateMap<BlogViewModels, BlogArticle>(); 
//如果不想一個一個的配置,可以用接口的形式,批量導(dǎo)入
//這是一個思路,我沒有具體去寫,留個坑吧
//public interface IMapperTo<TDestination>{}
//然后同樣來個Profile集中處理這些interface
/// <summary>
/// 根據(jù)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的話自己擴(kuò)展咯
            .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實現(xiàn)模型映射,并注入

老規(guī)矩,還是在Startup中,注入服務(wù)

services.AddAutoMapper(typeof(Startup));//這是AutoMapper的2.0新特性

修改上邊服務(wù)層BlogArticleServices.cs 中g(shù)etBlogDetails 方法中的賦值,改用AutoMapper,并用構(gòu)造函數(shù)注入
最終的代碼是:

// 依賴注入 
 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)方法,運行項目,斷點調(diào)試,發(fā)現(xiàn)已經(jīng)成功了,是不是很方便,你也可以反過來試一試

        [HttpGet("{id}", Name = "Get")]
        public async Task<object> Get(int id)
        {
            var model = await _blogArticleServices.getBlogDetails(id);//調(diào)用該方法,這里 _blogArticleServices 是依賴注入的實例,不是類
            var data = new { success = true, data = model };
            return data;
        }
image.png

好啦,到目前為止,咱們已經(jīng)注入了這些服務(wù)了:

image.png

6、復(fù)雜深拷貝映射

有的小伙伴問,你這個這個簡單,都是相同字段的,那當(dāng)然很方便啦,要是一個復(fù)雜的,比如屬性名字不一樣的,或者說有子類等嵌入型的咋辦?放心,一樣是可以的,請看

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、如果是還有子類的復(fù)雜類型
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項目打包發(fā)布在IIS

1、項目打包發(fā)布

在項目Blog.Core中,右鍵,發(fā)布,選擇文件,相信大家都會,不會的可以聯(lián)系我

image
image

注意1: 這里有一個坑,還記得我們用swagger中使用的兩個xml文件,記得是兩個文件,編譯的時候有,但是.net core官方限制了在發(fā)布的時候包含xml文件,所以我們需要處理下

在發(fā)布之前,我們手動在項目工程文件 blog.core.csproj中,增加

<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

或者,右鍵xml文檔,屬性,始終復(fù)制,這些都是 net 的基本技能,有問題百度下就知道了。


當(dāng)然我們還可以基于CLI的Publish命令進(jìn)行發(fā)布,只需切換到Light.API根目錄下,輸入以下命令即可:

dotnet publish --framework netcoreapp1.1 --output "E:\Publish" --configuration Release

framework表示目標(biāo)框架,output表示要發(fā)布到的目錄文件夾,configuration表示配置文件,等同于和上面我們通過管理器來發(fā)布的操作

具體的大家可以自行實驗

注意2:如果你想發(fā)布到其他文件夾,可以使用生成命令:

是因為我在 api 層的項目屬性中,配置了生成命令:


image

Copy "(ProjectDir)bin\Debug\netcoreapp2.2\" "(SolutionDir)Blog.Core\bin\Debug"

提示:

我們發(fā)布項目的時候,會生成一個web.config文件,這個web.config文件是為了IIS而作用的,如果用基于CLI的dotnet命令啟動,則不需要這個config。

2、安裝運行時Runtime(只能運行.net core應(yīng)用程序,不能開發(fā))

比如服務(wù)器里,可以僅僅安裝運行時即可,如果不安裝,你可能會遇到這個錯誤:

image

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

image

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

image

注意:如果你是本地開發(fā),還要安裝SDK,下文會提到,如果只想服務(wù)器中運行,只安裝上邊的運行時即可,(這里的運行是能dotnet xxx.dll跑起來,而不是命令行dotnet run啟動)

比如你安裝后,輸入 dotnet --version 會報錯,下邊這個錯誤需要安裝 SDK,不用理會,只要保證 dotnet 的命令 能正常就行

image

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

image

至于為啥沒有安裝成功,我知道的三點:

1、沒有重啟

2、有的服務(wù)器是x64的,但是需要安裝x86的

3、執(zhí)行命令,dotnet --list-runtimes 沒有找到相應(yīng)的版本

3、安裝SDK(windows服務(wù)器不用安裝)

https://dotnet.microsoft.com/download/visual-studio-sdks?utm_source=getdotnetsdk&utm_medium=referral
https://www.microsoft.com/net/learn/dotnet/hello-world-tutorial

提示:

我們發(fā)布項目的時候,會生成一個web.config文件,這個web.config文件是為了IIS而作用的,如果用基于CLI的dotnet命令啟動,則不需要這個config。

2、安裝運行時Runtime(只能運行.net core應(yīng)用程序,不能開發(fā))

比如服務(wù)器里,可以僅僅安裝運行時即可,如果不安裝,你可能會遇到這個錯誤:

image

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

image

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

image

注意:如果你是本地開發(fā),還要安裝SDK,下文會提到,如果只想服務(wù)器中運行,只安裝上邊的運行時即可,(這里的運行是能dotnet xxx.dll跑起來,而不是命令行dotnet run啟動)

比如你安裝后,輸入 dotnet --version 會報錯,下邊這個錯誤需要安裝 SDK,不用理會,只要保證 dotnet 的命令 能正常就行

image

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

image

至于為啥沒有安裝成功,我知道的三點:

1、沒有重啟

2、有的服務(wù)器是x64的,但是需要安裝x86的

3、執(zhí)行命令,dotnet --list-runtimes 沒有找到相應(yīng)的版本

3、安裝SDK(windows服務(wù)器不用安裝)

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托管模塊(已安裝則跳過),

  下載地址:點擊我下載

image

5、應(yīng)用池配置為無托管代碼

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

image

老張:如果需要讀寫根目錄權(quán)限,要更改應(yīng)用池 ApplicationPoolIdentity

6、可以打開錯誤日志

在發(fā)布的時候,會有一個web.config出現(xiàn),通過修改web.config 啟用錯誤日志查看詳細(xì)錯誤信息

將stdoutLogEnabled的修改為 true,并在應(yīng)用程序根目錄添加 logs 文件夾

一定要手動添加logs文件,不然會不出現(xiàn)

但是這個文件名應(yīng)該不能被修改:

image

7、只要本地能通過,常見的錯誤就是生成的文件不全導(dǎo)致的,大家可以自行看看,如果有問題,也可以大家一起解決

比如錯誤

1、缺少一個補(bǔ)丁

image

其中一個問題是少一個補(bǔ)丁,發(fā)現(xiàn)需要打個補(bǔ)?。╓indows6.1-KB2533623-x64.msu),下載地址:點我點我。

8、在IIS中啟動項目,或者直接輸入服務(wù)器IP地址,加端口調(diào)試

注意:這里有一個小問題,因為發(fā)布以后,默認(rèn)啟動頁是在開發(fā)環(huán)境中重定向到了swagger,但是在服務(wù)器部署以后,不能跳轉(zhuǎn),大家打開后會這樣,404找不到,不要怕,

只需要在后邊加上Swagger就行了

image

9、配置域名

當(dāng)前端口配置域名的時候,需要在IIS的應(yīng)用程序池中,修改“加載用戶配置文件”為 True

image

三、項目在Liunx Ubuntu中部署(簡單版,慢慢完善)

1、在騰訊云購買Ubuntu服務(wù)器后,登陸,然后進(jìn)入命令頁面

image

2、部署Linux系統(tǒng)中的微軟環(huán)境

繼續(xù)執(zhí)行下面的命令

Register the trusted Microsoft signature key:

curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg

繼續(xù)

sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg

根據(jù)系統(tǒng)版本,執(zhí)行下面的命令

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'

好了,環(huán)境部署完畢,下面我們安裝 SDK

3、部署.Ne Core 環(huán)境

sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1.4
安裝成功后,輸入命令 dotnet

image

證明安裝成功啦

4、安裝代碼上傳工具,F(xiàn)illzila或者winSCP都可以,(我用的是winSCP)

軟件下好打開后界面是這樣的,我們需要填的就是主機(jī)名(你服務(wù)器的公網(wǎng)IP)、用戶名(服務(wù)器的用戶名)、密碼(你買服務(wù)器時設(shè)置的密碼),那個文件協(xié)議就是SFTP,不用改變

image

5、登陸進(jìn)去默認(rèn)是 /Home/ubuntu 文件夾,我們都在這里操作

image

6、下面我們在服務(wù)器新建一個控制臺項目測試一下

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word;">dotnet new console -o myApp
</pre>

image

然后就在winSCP發(fā)現(xiàn)多了一個項目

image

** 7、然后運行我們剛剛創(chuàng)建的項目**

cd myApp
dotnet run

image

代碼一起正常!

8、把我們的項目發(fā)布上去,注意這里不是咱們發(fā)布的版本!不是發(fā)布的版本!

因為我們本地發(fā)布的是windows版本的,如果把publish打包版本發(fā)布上去,會報錯,各種錯

所以應(yīng)該是把整個解決方法提交上去,當(dāng)然git就別提交了

image

然后呢,進(jìn)入到我們要發(fā)布的接口層項目

cd Blog.Core,然后再cd Blog.Core

最后執(zhí)行 dotnet run 即可

image

四、發(fā)布到Ubuntu

參考文章 @發(fā)布 ASP.NET Core 2.x 應(yīng)用到 Ubuntu

1、安裝.NET Core

首先需要安裝.NET Core Runtime: https://www.microsoft.com/net/download

image

點擊之后,根據(jù)您的Linux發(fā)行版不同,選擇相應(yīng)的操作步驟:

image
image

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

image

2、安裝Nginx

另外還需要安裝Nginx,直接查看官網(wǎng)文檔吧: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,如果看到如下效果說明安裝成功:

image

3、在服務(wù)器構(gòu)建源碼并發(fā)布

然后就是發(fā)布程序了,發(fā)布有兩種辦法:

  • 在開發(fā)機(jī)上執(zhí)行dotnet publish然后把發(fā)布的文件復(fù)制到服務(wù)器上
  • 或者直接在服務(wù)器上使用源碼構(gòu)建并發(fā)布,我一般是這樣做的。

由于我是直接在服務(wù)器上構(gòu)建發(fā)布,所以我需要安裝.NET Core SDK:https://www.microsoft.com/net/learn/get-started-with-dotnet-tutorial

image

然后就可以使用發(fā)布命令了:dotnet publish --configuration Release

發(fā)布好的文件在bin/Release/netcoreapp./publish**下面。

再把publish下的所有文件復(fù)制到我的目標(biāo)文件夾即可:

image

在我的目標(biāo)目錄下,有這些文件:

image

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

image

4、配置Nginx

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

image

保存后執(zhí)行sudo nginx -t檢驗這個配置文件。

然后再執(zhí)行 nginx -s reload 來重啟nginx。

隨后需要再把發(fā)布后的程序運行一下:dotnet test.dll:

image

在我使用網(wǎng)址訪問80端口的時候,會自動跳轉(zhuǎn)到5001端口,導(dǎo)致連接失?。?/p>

image

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

image

重復(fù)上述步驟之后,通過網(wǎng)址的80端口,就可以正常訪問了:

image

5、NGINX配置證書和HTTPS

配置HTTPS和證書相關(guān)的內(nèi)容直接去看官方文檔: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正常訪問網(wǎng)站,那么有可能是無法綁定443端口導(dǎo)致的。

查看nginx錯誤日志:/var/log/nginx/error.log,如果出現(xiàn)下面的錯誤:

image

可以執(zhí)行下列命令來解決:

sudo fuser -k 443/tcp
service nginx restart

然后再次訪問https網(wǎng)址:

image

這樣就可以正常訪問https的網(wǎng)址了。

五、NetCore 部署到 WINDOWS服務(wù)

微軟有提供 如何在windows服務(wù)托管asp.net core ,不過步驟比較麻煩,還需要改源碼,網(wǎng)上找到一種方法 使用NSSM把.Net Core部署至windows服務(wù)

簡單說一下步驟

1. 下載nssm:http://www.nssm.cc/download

2. 運行cmd,定位到nssm.exe文件路徑,運行nssm install

3. 在彈出的窗口配置:

Path:dotnet所在的目錄,一般默認(rèn)是在C:\Program Files\dotnet\dotnet.exe;

Startup directory:程序所在的目錄,就是最后程序dll所在的目錄;

Arguments:程序dll的名稱,一般是項目名加上.dll;

Service name:在此寫上服務(wù)的名稱即可。

最后點擊install service 完成windows服務(wù)安裝。

在windows服務(wù)找到對應(yīng)服務(wù)名,啟動,然后根據(jù)launchSettings.json配置的端口訪問,即可調(diào)取接口。

image

六、結(jié)語

今天暫時就先寫到這里,我們學(xué)到了如何用AutoMapper來實現(xiàn)DTO數(shù)據(jù)對象映射,也學(xué)會了在windows下的IIS中發(fā)布項目,最后就是Linux系統(tǒng)中,搭建環(huán)境和運行.net core 。以后呢我還會講到Docker 容器,Nginx代理等等,大家拭目以待吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容