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

原文作者:老张的哲学

零、今天完成右下角的深蓝色部分

image.png

一、在项目中使用添加一个案例使用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,
            };    

所以这个方法的全部代码是:
接口层也要添加:

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

想了想这才是一个方法,一般的系统都会有少则几十,多则上百个这样的方法,这还不算,大家肯定遇到过一个情况,如果有一天要在页面多显示一个字段,噗!不是吧,首先要存在数据库,然后在该实体类就应该多一个,然后再在每一个赋值的地方增加一个,而且也没有更好的办法不是,一不小心就少了一个,然后被产品测试说咱们不细心,心塞哟,别慌!神器来了,一招搞定。

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,用来匹配所有的映射对象关系

using AutoMapper;
using Blog.Core.Model;

namespace Blog.Core.API.AutoMapper
{
    /// <summary>
    /// 配置构造函数,用来创建关系映射
    /// </summary>
    public class CustomProfile : Profile
    {
        public CustomProfile()
        {
            CreateMap<Article, ViewModels>();
        }
    }
}

大家看下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;
        }
image.png

好啦,到目前为止,咱们已经注入了这些服务了:

image.png

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中,右键,发布,选择文件,相信大家都会,不会的可以联系我

image
image

注意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 层的项目属性中,配置了生成命令:


image

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

提示:

我们发布项目的时候,会生成一个web.config文件,这个web.config文件是为了IIS而作用的,如果用基于CLI的dotnet命令启动,则不需要这个config。

2、安装运行时Runtime(只能运行.net core应用程序,不能开发)

比如服务器里,可以仅仅安装运行时即可,如果不安装,你可能会遇到这个错误:

image

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

image

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

image

注意:如果你是本地开发,还要安装SDK,下文会提到,如果只想服务器中运行,只安装上边的运行时即可,(这里的运行是能dotnet xxx.dll跑起来,而不是命令行dotnet run启动)

比如你安装后,输入 dotnet --version 会报错,下边这个错误需要安装 SDK,不用理会,只要保证 dotnet 的命令 能正常就行

image

怎么保证安装好了呢,直接在服务器中的项目目录下,执行 dotnet xxxx.dll,通过kestrel服务器运行,如果正常则安装成功,可以继续配置iis,如果报错,就是没有安装成功。

image

至于为啥没有安装成功,我知道的三点:

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

提示:

我们发布项目的时候,会生成一个web.config文件,这个web.config文件是为了IIS而作用的,如果用基于CLI的dotnet命令启动,则不需要这个config。

2、安装运行时Runtime(只能运行.net core应用程序,不能开发)

比如服务器里,可以仅仅安装运行时即可,如果不安装,你可能会遇到这个错误:

image

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

image

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

image

注意:如果你是本地开发,还要安装SDK,下文会提到,如果只想服务器中运行,只安装上边的运行时即可,(这里的运行是能dotnet xxx.dll跑起来,而不是命令行dotnet run启动)

比如你安装后,输入 dotnet --version 会报错,下边这个错误需要安装 SDK,不用理会,只要保证 dotnet 的命令 能正常就行

image

怎么保证安装好了呢,直接在服务器中的项目目录下,执行 dotnet xxxx.dll,通过kestrel服务器运行,如果正常则安装成功,可以继续配置iis,如果报错,就是没有安装成功。

image

至于为啥没有安装成功,我知道的三点:

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托管模块(已安装则跳过),

  下载地址:点击我下载

image

5、应用池配置为无托管代码

(网上解释:ASP.NET Core不再是由IIS工作进程(w3wp.exe)托管,而是使用自托管Web服务器(Kestrel)运行,IIS则是作为反向代理的角色转发请求到Kestrel不同端口的ASP.NET Core程序中,随后就将接收到的请求推送至中间件管道中去,处理完你的请求和相关业务逻辑之后再将HTTP响应数据重新回写到IIS中,最终转达到不同的客户端(浏览器,APP,客户端等)。而配置文件和过程都会由些许调整,中间最重要的角色便是AspNetCoreModule,它是其中一个的IIS模块,请求进入到IIS之后便立即由它转发,并迅速重定向到ASP.NET Core项目中,所以这时候我们无需设置应用程序池来托管我们的代码,它只负责转发请求而已)

image

老张:如果需要读写根目录权限,要更改应用池 ApplicationPoolIdentity

6、可以打开错误日志

在发布的时候,会有一个web.config出现,通过修改web.config 启用错误日志查看详细错误信息

将stdoutLogEnabled的修改为 true,并在应用程序根目录添加 logs 文件夹

一定要手动添加logs文件,不然会不出现

但是这个文件名应该不能被修改:

image

7、只要本地能通过,常见的错误就是生成的文件不全导致的,大家可以自行看看,如果有问题,也可以大家一起解决

比如错误

1、缺少一个补丁

image

其中一个问题是少一个补丁,发现需要打个补丁(Windows6.1-KB2533623-x64.msu),下载地址:点我点我

8、在IIS中启动项目,或者直接输入服务器IP地址,加端口调试

注意:这里有一个小问题,因为发布以后,默认启动页是在开发环境中重定向到了swagger,但是在服务器部署以后,不能跳转,大家打开后会这样,404找不到,不要怕,

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

image

9、配置域名

当前端口配置域名的时候,需要在IIS的应用程序池中,修改“加载用户配置文件”为 True

image

三、项目在Liunx Ubuntu中部署(简单版,慢慢完善)

1、在腾讯云购买Ubuntu服务器后,登陆,然后进入命令页面

image

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

image

证明安装成功啦

4、安装代码上传工具,Fillzila或者winSCP都可以,(我用的是winSCP)

软件下好打开后界面是这样的,我们需要填的就是主机名(你服务器的公网IP)、用户名(服务器的用户名)、密码(你买服务器时设置的密码),那个文件协议就是SFTP,不用改变

image

5、登陆进去默认是 /Home/ubuntu 文件夹,我们都在这里操作

image

6、下面我们在服务器新建一个控制台项目测试一下

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

image

然后就在winSCP发现多了一个项目

image

** 7、然后运行我们刚刚创建的项目**

cd myApp
dotnet run

image

代码一起正常!

8、把我们的项目发布上去,注意这里不是咱们发布的版本!不是发布的版本!

因为我们本地发布的是windows版本的,如果把publish打包版本发布上去,会报错,各种错

所以应该是把整个解决方法提交上去,当然git就别提交了

image

然后呢,进入到我们要发布的接口层项目

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

最后执行 dotnet run 即可

image

四、发布到Ubuntu

参考文章 @发布 ASP.NET Core 2.x 应用到 Ubuntu

1、安装.NET Core

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

image

点击之后,根据您的Linux发行版不同,选择相应的操作步骤:

image
image

最后执行dotnet --info验证安装是否成功:

image

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,如果看到如下效果说明安装成功:

image

3、在服务器构建源码并发布

然后就是发布程序了,发布有两种办法:

  • 在开发机上执行dotnet publish然后把发布的文件复制到服务器上
  • 或者直接在服务器上使用源码构建并发布,我一般是这样做的。

由于我是直接在服务器上构建发布,所以我需要安装.NET Core SDK:https://www.microsoft.com/net/learn/get-started-with-dotnet-tutorial

image

然后就可以使用发布命令了:dotnet publish --configuration Release

发布好的文件在bin/Release/netcoreapp./publish**下面。

再把publish下的所有文件复制到我的目标文件夹即可:

image

在我的目标目录下,有这些文件:

image

如果执行 dotnet test.dll,这个程序就会在localhost:5000运行:

image

4、配置Nginx

然后我们再回来配置Nginx,进入/etc/nginx/sites-available,里面有一个Default文件,把它改个名,然后我们再建立一个新的Default文件:

image

保存后执行sudo nginx -t检验这个配置文件。

然后再执行 nginx -s reload 来重启nginx。

随后需要再把发布后的程序运行一下:dotnet test.dll:

image

在我使用网址访问80端口的时候,会自动跳转到5001端口,导致连接失败:

image

这是因为项目里默认使用了HTTPS Redirection。因为我没有证书,所以为了演示,我把HTTPS Redirection相关的代码注释掉,再发布:

image

重复上述步骤之后,通过网址的80端口,就可以正常访问了:

image

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,如果出现下面的错误:

image

可以执行下列命令来解决:

sudo fuser -k 443/tcp
service nginx restart

然后再次访问https网址:

image

这样就可以正常访问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配置的端口访问,即可调取接口。

image

六、结语

今天暂时就先写到这里,我们学到了如何用AutoMapper来实现DTO数据对象映射,也学会了在windows下的IIS中发布项目,最后就是Linux系统中,搭建环境和运行.net core 。以后呢我还会讲到Docker 容器,Nginx代理等等,大家拭目以待吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容