ASP.NET Core 快速入门(实战篇)

Posted DotNet

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET Core 快速入门(实战篇)相关的知识,希望对你有一定的参考价值。


来源:农码一生

cnblogs.com/zhaopei/p/netcore2.html


上篇讲了。今天我们将做几个小玩意实战一下。用到的技术和工具有mysql、websocket、AngleSharp(爬虫html解析)、nginx多站点部署。


NO1 留言板(mysql的使用)


演示:http://haojima.net


这个功能很简单。就是对数据库的写入和展示。如果在Windows下,相信大家分分钟都可以搞定。而初次接触.net core + mysql可能需要注意些细节。


首先打开vs2017新建一个asp.net core项目(选Web应用程序),然后nuget 导入Microsoft.EntityFrameworkCore.Tools 1.1.1和MySql.Data.EntityFrameworkCore 8.0.8-dmr。


然后新建一个DbContext类。


public class DataContext : DbContext

{

    //【注意】连接字符串一定要加 sslmode=none 

    string str = @"Data Source=;Database=;

     User ID=;Password=;pooling=true;CharSet=utf8;port=3306;sslmode=none";

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>

        optionsBuilder.UseMySQL(str);

    //下面就可以添加要加入数据库的实体了

    //public DbSet<Message> Messages { get; set; }

}


到此为止,我们已经可以利用EF Core直接连接mysql进行增删改查操作了。注意:需要导入命名空间


using Microsoft.EntityFrameworkCore; 

using MySQL.Data.EntityFrameworkCore.Extensions;


当然。你会说,连接字符串不能硬编码到代码里面。我们也可以放配置文件。


appsettings.json

{

  "Logging": {

    "IncludeScopes": false,

    "LogLevel": {

      "Default": "Warning"

    }

  },

  "ConnectionStrings": { "SqlServerConnection": "Data Source=;Database=;User ID=;Password=;pooling=true;CharSet=utf8;port=3306;sslmode=none" }

}


然后把上面的硬编码注释掉。在Startup.cs文件的ConfigureServices方法添加


var connection = Configuration.GetConnectionString("SqlServerConnection");

services.AddDbContext<DataContext>(options => options.UseMySQL(connection));


注意:项目名称和路径最好不要有中文,不然会出现些乱七八糟的问题。


完整代码:

https://github.com/zhaopeiym/BlogDemoCode/tree/master/MessageBoard


NO2 聊天室(WebSocket的使用)


演示:http://socket.haojima.net


WebSocket是Html5新增的一个很酷的技术。下面我们简单讲解下这个很酷的技术


var Socket = new WebSocket(url);//创建 WebSocket 对象


创建了一个WebSocket对象后会触发打开连接事件:


Socket.onopen = function(){  }


除了onopen事件,还有其他三个事件:


Socket.onmessage  //客户端接收服务端数据时触发

Socket.onerror    //通信发生错误时触发

Socket.onclose    //连接关闭时触发


另外还有两个方法:


Socket.send()   //使用连接发送数据

Socket.close()  //关闭连接


最后还有四个连接状态属性:


Socket.readyState

0 - 表示连接尚未建立。

1 - 表示连接已建立,可以进行通信。

2 - 表示连接正在进行关闭。

3 - 表示连接已经关闭或者连接不能打开。


整个WebSocket常用功能知识点就四个事件、两个方法、四种状态。简单吧,下面我们看看asp.net core后台的配合:


后台添加一个SocketHandler类,并添加一个静态方法Map:


/// <summary>

/// 请求

/// </summary>

/// <param name="app"></param>

public static void Map(IApplicationBuilder app)

{

    app.UseWebSockets(); //【注意】需要 nuget   导入 Microsoft.AspNetCore.WebSockets.Server

    app.Use(Acceptor);

}


然后新增对应的Acceptor方法:


/// <summary>

/// 接收请求

/// </summary>

/// <param name="httpContext"></param>

/// <param name="n"></param>

/// <returns></returns>

static async Task Acceptor(HttpContext httpContext, Func<Task> n)

{


需要在Startup.cs类里面的Configure方法里面加入


app.Map("/ws", SocketHandler.Map);   //传入我们刚才新建的静态方法Map


现在为止,基本的类和配置已经完成。


我们主要操作,是在Acceptor方法里面接收和发送消息。


//建立连接

var socket = await httpContext.WebSockets.AcceptWebSocketAsync();

//等待接收数据

await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

//发送消息

await socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);


后台关键代码也就这三句,建立连接、等待接收、发送消息。


不过这里有一点需要理解。建立连接后,可以接收任意多次客户端消息。所以ReceiveAsync等待接收这里需要死循环接收消息,直到连接断开。


(不用担心真的死循环,没有消息发送的时候,代码会阻塞在那里等待消息)


完整实现:https://github.com/zhaopeiym/ChatRoom


NO3 找工作(AngleSharp的使用)


演示:http://job.haojima.net


对于爬虫抓包,我相信大家初次接触都非常的热衷于此。我也不例外。

那么在asp.net core下面是否也有这样的插件呢?答案是肯定的。


(感谢博主对.net core的贡献)。不过xpath用起来超级恶心。


之前在.NET下面有一款Jumony:


http://www.cnblogs.com/Ivony/p/3447536.html(博客园大牛写的)。


支持CSS选择和linq查询。简直不要太爽。


可是不支持.net core。(本人试了下迁移.net core,发现很多类在.net core没有实现)


最后还是到了一款支持.net core的解析组件。并可以媲美Jumony,同样支持css选择和linq查询。那就是AngleSharp。


新建项目,nuget 安装 AngleSharp。然后以下简单使用:


using (HttpClient http = new HttpClient())

{

    var htmlString = await http.GetStringAsync(url);

    HtmlParser htmlParser = new HtmlParser();

    var jobInfos = htmlParser.Parse(htmlString)

        .QuerySelectorAll(".newlist_list_content table")

        .Where(t => t.QuerySelectorAll(".zwmc a").FirstOrDefault() != null)

        .Select(t => new JobInfo()

        {

            PositionName = t.QuerySelectorAll(".zwmc a").FirstOrDefault().TextContent,

            CorporateName = t.QuerySelectorAll(".gsmc a").FirstOrDefault().TextContent,

            Salary = t.QuerySelectorAll(".zwyx").FirstOrDefault().TextContent,

            WorkingPlace = t.QuerySelectorAll(".gzdd").FirstOrDefault().TextContent,

        .ToList();

    return jobInfos;

}


看到没有,就像jq一样解析html。如果你说不爽我都不信。


完整实现:https://github.com/zhaopeiym/JobWanted


部署多个站点


以上,这些项目都比较简单。关键技术点和难点都进行的分析。我相信大家都可以动起手练习起来了。


不过有个问题,前面我们只说了部署一个应用程序。如果是多个该怎么部署呢?


首先我们把多个程序发布包放到服务器上。


然后修改nginx的配置文件/etc/nginx/conf.d/default.conf


server {

    listen 80;

    server_name www.haojima.net;           #对应的域名

    root /home/projects/messagBoard;       #程序路径

    location / {

        proxy_pass http://localhost:5000;  #内网端口

        proxy_http_version 1.1; 

        proxy_set_header Upgrade $http_upgrade;

        proxy_set_header Connection keep-alive;

        proxy_set_header Host $host;

        proxy_cache_bypass $http_upgrade;

        proxy_set_header X-real-ip $remote_addr;

        

        proxy_set_header Upgrade $http_upgrade;   

    }

}


有几个程序就添加几个server,不过需要修改你解析到的域名、程序路径和内网对应的端口(看配置里的注释) 。


然后修改supervisor的配置文件/etc/supervisor/conf.d/supervisord.conf


[program:MessageBoard]

command=dotnet MessageBoard.dll        ; 运行程序的命令

directory= /home/projects/messagBoard/ ; 命令执行的目录

autorestart=true                    ; 程序意外退出是否自动重启

stderr_logfile=/var/log/WebApplication1.err.log ; 错误日志文件

stdout_logfile=/var/log/WebApplication1.out.log ; 输出日志文件

environment=ASPNETCORE_ENVIRONMENT=Production ; 进程环境变量

user=root ; 进程执行的用户身份

stopsignal=INT


有几个程序就往下复制几份program。需要修改program名称,只要名称不重复就可以。然后修改 运行程序的命令 对应的dll和命令执行的目录(看配置文件的注释)。


如此就可以部署多个程序了。


开始我还以为是在域名解析的时候,解析IP + 端口。原来是多个域名解析到同一个IP,然后nginx在内部做域名和内网端口分发。


一些其它的细节


部署阿里云


我们在linux的防火墙开放了端口,发现在外面还是访问不了(可以telnet IP 端口 来测试)。有可能是阿里云拦截了。https://help.aliyun.com/document_detail/25471.html 在安全组添加某端口哪些IP可以访问。


mysql的客户端


对于mysql,我们安装好之后总不能每次命令操作吧。在Windows下面有个客户端Navicat可以方便管理mysql。


下载链接:http://pan.baidu.com/s/1nvMEQQt 密码:5eao


获取ip


用了nginx后发现取不到浏览器IP了。那是因为我们程序都是浏览器访问nginx,然后nginx转发内网程序端口。所以取到的IP都是内网本机IP。如果需要取浏览器IP需要在nginx配置


server {

    listen 80;

    server_name www.haojima.net;

    root /home/projects/messagBoard;

    location / {

        proxy_pass http://localhost:5000;

        proxy_http_version 1.1; 

        proxy_set_header Upgrade $http_upgrade;

        proxy_set_header Connection keep-alive;

        proxy_set_header Host $host;

        proxy_cache_bypass $http_upgrade;

        proxy_set_header X-real-ip $remote_addr;     # 新添加

    }

}


然后代码里面取IP:


 var ip = HttpContext.Request.Headers["X-real-ip"].FirstOrDefault();


WebSocket在nginx的配置


上面我们写的WebSocket直接运行发现没有任何问题,可是部署在nginx去跑不起来了。那是因为需要nginx支持WebSocket,需要配置。

http://nginx.org/en/docs/http/websocket.html


server {

    listen 80;

    server_name job.haojima.net;

    root /home/projects/jobWanted;

    location / {

        proxy_pass http://localhost:5002;

        proxy_http_version 1.1;

        proxy_set_header Upgrade $http_upgrade;

        proxy_set_header Connection keep-alive;

        proxy_set_header Host $host;

        proxy_cache_bypass $http_upgrade;

        proxy_set_header X-real-ip $remote_addr;

        proxy_set_header Upgrade $http_upgrade;     # 新增

        proxy_set_header Connection "upgrade";      # 新增 

    }

}


WebSocket心跳


经过上面的配置,我们的WebSocket在nginx上跑起来了。万分欢喜的我们,发现一分钟不发消息就自动掉线了。郁闷至极到头大。细心的同学通过上面的链接资料其实已经有说明:


By default, the connection will be closed if the proxied server does not transmit any data within 60 seconds. This timeout can be increased with the proxy_read_timeout directive. Alternatively, the proxied server can be configured to periodically send WebSocket ping frames to reset the timeout and check if the connection is still alive.


默认情况下,如果代理的服务器在60秒内没有传输任何数据,则连接将被关闭。可以使用proxy_read_timeout指令增加此超时 。或者,代理服务器可以配置为定期发送WebSocket ping帧以重置超时并检查连接是否仍然存在。


nginx给出了两种解决方案。第一种,修改proxy_read_timeout (默认60秒)。第二种,浏览器客户端定时发送心跳包(时间要短于proxy_read_timeout)。

我使用的是第二种方式。


第一种虽然简单粗暴,但是时间再长也是一个值,还是会有超时的可能。再者,谁能保证浏览器端不会new 很多个WebSocket出来捣蛋。


第二种方式,浏览器定时发送一条消息,内容和后台约定下。如发送“心跳”,然后后台接收消息是,判断如果是“心跳”则不做任何处理。


中文编码


在做“找工作”爬前程无忧的数据时,发现他们使用的GBK编码。而在.net core中默认不支持这种格式,导致取到的数据都是乱码。


我们需要nuget安装System.Text.Encoding.CodePages。然后在Startup.cs的Configure里面注册:


Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);//注册编码提供程序


使用:


var htmlBytes = await http.GetByteArrayAsync(url);

var htmlString = Encoding.GetEncoding("GBK").GetString(htmlBytes);


ASP.NET Core 端口分配


asp.net core 默认端口都是5000。那么我们运行第二个程序的时候就会提示5000端口被占用。这个时候,我们就需要为每个程序分配不同的端口了。

在根目录新建一个json文件hosting.json


{

  "server.urls": "http://*:5002"

}


在Program.cs文件修改


public static void Main(string[] args)

{

    var config = new ConfigurationBuilder()

          .SetBasePath(Directory.GetCurrentDirectory())

          .AddJsonFile("hosting.json", optional: true)

          .Build();


    var host = new WebHostBuilder()

        .UseKestrel()

        .UseConfiguration(config)

        .UseContentRoot(Directory.GetCurrentDirectory())

        .UseIISIntegration()

        .UseStartup<Startup>()

        .UseApplicationInsights()

        .Build();


    host.Run();

}


爬拉勾数据


在爬拉勾网的时候没有搞定,不知道是不是因为https的原因。


using (HttpClient http = new HttpClient())

{

    var url = "https://www.lagou.com/zhaopin/Java/?labelWords=label";

    var htmlString = await http.GetStringAsync(url);

}


在.net core中报错:An unhandled exception occurred while processing the request.


在.net 4.5 中抓到的数据是“页面加载中...”。和浏览器访问的结果不一样。

原因未知。如果有大佬解惑,感激不尽!

 

参考

http://www.runoob.com/html/html5-websocket.html

http://www.cnblogs.com/liguobao/p/6130121.html

http://www.cnblogs.com/linezero/p/5806814.html


演示

http://haojima.net

http://socket.haojima.net

http://job.haojima.net


源码

https://github.com/zhaopeiym/JobWanted

https://github.com/zhaopeiym/ChatRoom

https://github.com/zhaopeiym/BlogDemoCode


看完本文有收获?请转发分享给更多人

关注「DotNet」,提升.Net技能 

以上是关于ASP.NET Core 快速入门(实战篇)的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core快速入门Middleware管道介绍自己动手构建RequestDelegate管道

ASP.NET Core快速入门Middleware管道介绍自己动手构建RequestDelegate管道

目录ASP.NET Core 2.1 入门教程

第16章 使用ASP.NET Core Identity

ASP.NET Core on K8S学习初探K8S基本概念快速一览

ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 14. ASP.NET Core Identity 入门