驾一叶之扁舟 举匏樽以相属
寄蜉蝣于天地,渺沧海之一粟。哀吾生之须臾,羡长江之无穷。
挟飞仙以遨游,抱明月而长终。知不可乎骤得,托遗响于悲风。

从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之十三 || DTOs 对象映射使用,项目部署Windows+Linux完整版

本文3.0版本文章

 本文内容,和netcore2.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

 

posted @ 2018-08-31 12:54  老张的哲学  阅读(16230)  评论(39编辑  收藏  举报
作者:老张的哲学
好好学习,天天向上
返回顶部小火箭
好友榜:
如果愿意,把你的博客地址放这里
jianshu.com/u/老张
SqlSugar codeisbug.com