C#Web开发之blazor体验
Posted asmcvc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#Web开发之blazor体验相关的知识,希望对你有一定的参考价值。
前言
先说结论:blazor
做Web开发绝对好东西。对于我个人来说,有两个重要的原因:
- 特别喜欢
C#
; - 特别不喜欢
javascript
;
在了解到blazor
居然可以使用c#
做Web开发且完全可以不用编写JavaScript
代码时,第一感受是,居然有这么懂自己的东西,果断尝鲜体验!
网上大概也了解了一下,基本上都说得过于官方和理论,不够简单直接,这里直接说调研的结论。
- 使用
blazor
做web开发非常地高效(包括前后端),而且还是在资料不多的情况下(生态不太好,但绝对是好东西)。 blazor
提供两种模式:WebAssembly
、Server
;- 建议使用
Server
模式,WebAssembly
慢的受不了,一个简单的demo首次启动和每次刷新时都要将近 10秒钟时间,而相同demo项目Server
模式基本是瞬间显示的。 WebAssembly
模式还有一个弊端:目前只有一个线程,容易出现 UI 界面卡死的问题。据说以后会改,谁知道啥时候呢。- 简单的项目没必要搞什么前后分离,前后端分离也要看项目大小,小项目直接用混合模式也挺好的。
- 简单项目使用
Server
模式就可以完全前后端一锅端了,非常的简单高效。 - 如果前端设计能像客户端界面开发那样有控件拖放,就无敌了。
笔者本身不是做web开发的,但是对全栈比较感兴趣,在前几年也接触和体验了一下SpringBoot
开发框架,参见:SpringBoot极简教程,因为个人不喜欢Java
更青睐c#
,所以体验之后弄清楚如何使用及流程也就作罢了,日后并没有再接触使用。但是有了之前的了解,至少有了web开发的基础知识储备,因此此次使用blazor
也会比较地快和顺利。这一次也会以一个具体的demo项目来做介绍:搭建一个局域网环境使用的APP管理平台
。
背景是这样的:在很久之前我们团队就想搭建一个这样的内部平台,但是我们都是客户端开发(非前端开发),对这块没有经验也不擅长,也就作罢了。我们是做安卓安全研究的,日常会遇到各种各样的样本,具有重复使用的价值,但是都散落在组内各人自己的电脑里,没有汇总起来,时间久了也容易找不到。如果能在内网搭建一个这样的样本管理平台就好了,遇到新样本的时候上传上去,需要的时候检索下载下来。我们也想过用共享文件夹
或ftp服务器
的形式搭建,但是这都是单纯的文件上传和下载,没有样本的基础信息,并没有使用开来。
但这个需求也不算是刚需,只能说有了更好。但是在遇到blazor
的时候,我就想大胆试一下,本身C#
的开发效率非常高,心想应该花不了多少时间,于是开始构思:
- 需要有文件上传的功能;
- 需要有对APP样本文件进行解析的功能,并做基础信息的展示;
- 能检索和下载样本文件;
本着快速编程实现的想法,就想着如果能找到现成的代码改一改就行了,于是从blazor文件上传
的关键词找到了这个demo:File Upload with Blazor WebAssembly and ASP.NET Core Web API,GitHub地址:CodeMazeBlog/blazor-series at blazor-file-upload。工程里面有两个项目,分别是server(服务端)和client(客户端)。这是一个前后端分离的项目,结构比较清晰,如果打算上手学习blazor,是一个非常好的入门练手项目。这是一个管理产品(product)的demo,基本上和我的需求类似,所以就拿这个开刀修改起来。遇到了一些坑:
- 这个前端是
WebAssembly
模式,问题很严重,就是前面提的速度超级慢和界面卡死问题; - 服务端连接的数据库是
SQL Server
的,这个遇到问题不好解决花了一些时间,网上mysql
的资料多一些,好在及时换成了MySQL
;
全程是以逆向工程的思路去实现的,遇到了非常多的问题,基本就是遇到一个解决一个,最终把项目跑起来了,效果图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SItJUAJL-1669256117186)(…/…/…/images/2022/blazor_demo.png)]
主要功能介绍:
- 打开链接
http://[ip地址]:5000/apps
即可显示样本列表; - 支持按照名称检索样本;
- 支持按照不同字段排序,默认按照更新时间倒叙显示;
- 支持一键浏览本地文件上传,服务端自动解析样本信息并入库;
- 支持样本信息的删除和更新;
- 默认局域网内http方式无登录授权访问;
- 点击图标下载样本到本地电脑;
后面的教程会以正向工程的顺序讲解,然后穿插一些实际修改demo项目的经验和问题。
一、数据库配置
1、数据库软件安装
- MariaDB,账号密码弄个测试的简单一点的:root/123
- 安装:ODBC Connector - MariaDB Knowledge Base
- Navicat:可视化操作数据库,不用敲
SQL
命令,方面快捷;
2、VisualStudio连接MySQL
VisualStudio
默认使用的数据库是SQL Server
,先改为链接MySQL
的数据库,这里使用开源的MariaDB
。VisualStudio
链接MariaDB
数据库需要通过ODBC方式,因此需要安装ODBC Connector - MariaDB Knowledge Base,然后进行ODBC数据源配置:
- 打开
ODBC数据源管理程序
(Win + R 输入命令:odbcad32
); - 添加,选择:
MariaDB ODBC 3.0 Driver
,Name填:MariaDB,下一步; - 选择:TCP/IP,ServerName填:127.0.0.1,User name和Password分别填:root 123
- Test DSN,提示成功说明配置正确,后面一路下一步即可。
- VisualStudio里打开菜单
工具
-连接到数据库
,选择:Microsoft ODBC 数据源
,刚刚的MariaDB
名称,就能在服务器资源管理器
里看到数据库链接了。
参考:
3、创建数据库和表
- 打开
MariaDB
控制台,创建数据库:CREATE DATABASE appdb;
然后source apps.sql
;,apps.sql
内容如下(可以在Navicat
里设计,比较方便):
/*
Navicat Premium Data Transfer
Source Server : 127.0.0.1
Source Server Type : MySQL
Source Server Version : 101100 (10.11.0-MariaDB)
Source Host : localhost:3306
Source Schema : appdb
Target Server Type : MySQL
Target Server Version : 101100 (10.11.0-MariaDB)
File Encoding : 65001
Date: 18/11/2022 00:16:42
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for apps
-- ----------------------------
DROP TABLE IF EXISTS `apps`;
CREATE TABLE `apps` (
`Id` bigint NOT NULL AUTO_INCREMENT,
`Name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`Package` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`Icon` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
`VersionName` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`VersionCode` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`ShellName` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`Tags` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`Memo` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`Operator` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`dbPath` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
`Url` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
`CreationDate` datetime(6) NOT NULL DEFAULT current_timestamp(6),
`ModifiedDate` datetime(6) NOT NULL DEFAULT current_timestamp(6) ON UPDATE CURRENT_TIMESTAMP(6),
`Md5` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '',
`FileSize` int UNSIGNED NULL DEFAULT 0,
PRIMARY KEY (`Id`) USING BTREE,
UNIQUE INDEX `index_id`(`Id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of apps
-- ----------------------------
INSERT INTO `apps` VALUES (36, '测试App', 'com.test', 'https://appimg.dbankcdn.com/application/icon144/629336112d984dc1950f3992e0efdae0.png', '1.1', '2133', '360', NULL, '这个包有xxx的问题,版本发布的时候可以测一下', 'zhangsan', 'share\\\\app\\\\dytt8.apk', 'file:\\\\\\\\192.168.1.2\\\\share\\\\app\\\\dytt8.apk', '2022-11-17 23:44:00.872375', '2022-11-17 23:55:03.068345', NULL, 1973292);
INSERT INTO `apps` VALUES (38, '测试App', 'com.test', 'https://appimg.dbankcdn.com/application/icon144/629336112d984dc1950f3992e0efdae0.png', '1.1', '2133', '无', '加固 隐私', '这个包有xxx的问题,版本发布的时候可以测一下', 'zhangsan', 'share\\\\app\\\\184543_3.apk', 'file:\\\\\\\\192.168.1.2\\\\share\\\\app\\\\184543_3.apk', '2022-11-17 23:47:57.061125', '2022-11-18 00:10:47.843875', NULL, 46668230);
INSERT INTO `apps` VALUES (39, '测试App', 'com.test', 'https://appimg.dbankcdn.com/application/icon144/629336112d984dc1950f3992e0efdae0.png', '1.1', '2133', '无', NULL, '这个包有xxx的问题,版本发布的时候可以测一下', 'zhangsan', 'share\\\\app\\\\dytt8.apk', 'file:\\\\\\\\192.168.1.2\\\\share\\\\app\\\\dytt8.apk', '2022-11-18 00:02:13.713289', '2022-11-18 00:02:13.715084', NULL, 1973292);
SET FOREIGN_KEY_CHECKS = 1;
-
创建一个名为
AppInfo
的Model:public class AppInfo //[Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long Id get; set; // App名称 //[Required(ErrorMessage = "Name is required field")] [StringLength(maximumLength: 64)] public string Name get; set; // 包名 //[Required(ErrorMessage = "Package is required field")] [StringLength(maximumLength: 100, MinimumLength = 3)] public string Package get; set; // 图标 public string Icon get; set; // 版本号 [StringLength(maximumLength: 32)] public string VersionName get; set; [StringLength(maximumLength: 32)] public string VersionCode get; set; // 壳特征名称 [StringLength(maximumLength: 32)] public string ShellName get; set; = "null"; // 标签 [StringLength(maximumLength: 64)] public string Tags get; set; // 备注 [StringLength(maximumLength: 128)] public string Memo get; set; // 操作人员 [StringLength(maximumLength: 32)] public string Operator get; set; // 数据库存储路径(相对路径) public string dbPath get; set; public string Url get; set; public DateTime CreationDate get; set; // 对应的是自动更新时间的数据库字段 [DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTime ModifiedDate get; set; public string Md5 get; set; public long FileSize get; set;
-
服务端项目(
BlazorDemo.Server
)添加三方库:Pomelo.EntityFrameworkCore.MySql
,然后修改Startup.cs
函数ConfigureServices
的代码为:var connectionString = Configuration.GetConnectionString("sqlConnection"); services.AddDbContext<ProductContext>(opt => opt.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
-
服务端
appsettings.json
修改为:"Logging": "LogLevel": "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", "Microsoft.EntityFrameworkCore": "Information" , "ConnectionStrings": "sqlConnection": "SERVER=127.0.0.1;UID=root;PWD=123; database=appdb;" , "AllowedHosts": "*"
4、数据库迁移操作
在VisualStudio的PM控制台下输入命令。
Add-Migration Initial
Update-Database
Remove-Migration
二、服务器配置
一共需要设置 4 个服务器,分别是:
-
部署前端的服务器;
-
部署API接口的服务器;
-
数据库服务器;
-
文件下载服务器;
其实前两个服务器可以合并为一个。因为我们是内网使用,所有的服务器最后都用了同一台Windows普通电脑来完成。
先说最简单的文件服务器,直接使用Python
的一句话命令就可以完成了。在电脑上选择一个目录作为存储样本文件使用,例如E:\\share
,然后在该目录下创建一个批处理文件httpServer.bat
,内容:python -m http.server
,每次需要启动文件服务器的时候运行下httpServer.bat
即可。如果全部使用dotnet
技术栈,内容也可以是:
Rem python -m http.server
Rem dotnet tool install --global dotnet-serve
dotnet-serve -p 8000 -a 0.0.0.0
前后端可以分离开发,也可以混合开发。一开始借鉴的开源代码是前后端分离的,但是后来因为修改成了server模式,变成了混合模式。但是无论哪种都可以把前后端的项目部署在同一台服务器上,仅仅是开放的端口不一样罢了。最终配置如下:
-
服务端(
BlazorDemo.Server
)的launchSettings.json
内容:"iisSettings": "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": "applicationUrl": "http://localhost:52497", "sslPort": 0 , "profiles": "BlazorDemo.Server": "commandName": "Project", "launchBrowser": false, "applicationUrl": "http://localhost:5010;http://*:5010", "environmentVariables": "ASPNETCORE_ENVIRONMENT": "Development"
-
前端(
BlazorDemo.Client
)的appsettings.json
内容:"Logging": "LogLevel": "Default": "Information", "Microsoft.AspNetCore": "Warning" , "AllowedHosts": "*"
三、支持局域网访问
https的配置比较麻烦,而且第一次玩这个,不想太花时间折腾,本着先跑通的想法,先简单弄起来。如果是新创建项目的话,就不要勾选https
即可。
-
server项目里面
Startup.cs
文件Configure
函数注释掉这两句调用://app.UseHttpsRedirection(); //app.UseAuthorization();
-
server项目
launchSettings.json
去掉对https的支持,修改为:"iisSettings": "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": "applicationUrl": "http://localhost:52497", "sslPort": 0 , "profiles": "BlazorProducts.Server": "commandName": "Project", "launchBrowser": false, "applicationUrl": "http://localhost:5010;http://*:5010", "environmentVariables": "ASPNETCORE_ENVIRONMENT": "Development"
-
client项目的
launchSettings.json
去掉对https的支持,修改为:
"profiles":
"BlazorProducts.Client":
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"inspectUri": "wsProtocol://url.hostname:url.port/_framework/debug/ws-proxy?browser=browserInspectUri",
"applicationUrl": "http://localhost:5000;http://*:5000;",
"environmentVariables":
"ASPNETCORE_ENVIRONMENT": "Development"
- client项目代码里面凡是涉及到API接口调用的地方,一律修改为
http://作为服务器的电脑的具体IP地址:5010
四、界面组件
千万不要自己从零去写,没经验的话比较花时间,强烈推荐使用 Bootstrap Blazor - 企业级 UI 组件库 ,是个开源项目,GitHub地址见:BootstrapBlazor,总体使用比较方便,官网有详细使用教程和示例代码,直接拿来使用即可。文件上传功能就是直接用了这个 上传组件 Upload ,很方便,开发效率很高,使用的组件情况如下。
删除确认弹框:
<PopConfirmButton OnConfirm="()=>Apps.Remove(app); Delete(app); StateHasChanged(); return Task.CompletedTask; " Placement="Placement.Bottom" Color="Color.Danger" ConfirmIcon="fa-solid fa-triangle-exclamation text-danger" ConfirmButtonColor="Color.Danger" Text="删除" Content="确定删除数据吗?" />
文件上传:
<ButtonUpload BrowserButtonText="上传APP" TValue="string" IsMultiple="true" ShowProgress="true" OnChange="@OnClickToUpload" OnDelete="@(fileName => Task.FromResult(true))"></ButtonUpload>
private async Task OnClickToUpload(UploadFile file)
await SaveToFile(file);
消息通知(消息提示):
[Inject]
[NotNull]
private MessageService? MessageService get; set;
await MessageService.Show(new MessageOption()
Content = e.Message
);
五、全局异常处理
这个可以借鉴 深入浅出Blazor webassembly之异常错误显示,也可以直接使用 全局异常 - Bootstrap Blazor - 企业级 UI 组件库。
六、发布部署
发布
两种方法:
- 使用VisualStudio菜单:生成 - 发布,按照向导生成。
- 使用
dotnet
命令:dotnet publish -c Release
部署
-
选择一台Windows电脑作为服务器使用,记录其 IP 地址;
-
安装:dotnet、MariaDB(账号密码弄个测试的简单一点的:root/123)、ODBC Connector - MariaDB Knowledge Base
-
配置好数据库;
-
选择一个文件夹作为http服务器,目录下放好
httpServer.bat
,一键运行开启http服务。 -
为保证局域网的其他设备能够访问,客户端和服务端的
appsettings.json
做好类似如下配置:"AllowedHosts": "*", "Urls": "http://*:5010",
-
配置
ApkInfoTool
工具; -
启动服务端:
dotnet run
-
启动前端:
dotnet run
-
其他设备访问地址:
http://IP地址:5000
七、注意事项
-
服务器项目里的接口函数一律要用
anync Task
返回类型,否则容易出现错误:Cannot access a disposed context instance
,参考:Resolved- DBContext ObjectDisposedException:Cannot access a disposed of object -
WebAssembly
的缺点:不支持多线程容易UI卡死;启动和刷新速度特别慢; -
新建项目的时候勾选
ASP.NET Core托管
自动生成的解决方案包含三个项目,服务端也一并包含; -
回调函数用
await InvokeAsync
,例如:_timer = new Timer(async (sender) => await InvokeAsync(() => SearchCallback.InvokeAsync(SearchText); ); _timer.Dispose(); , null, 500, 0);
-
UI的响应函数类型要用
async Task
,如果内部没有调用异步函数,可以在最后调用下:await Task.CompletedTask;
,例如:public partial class Search private Timer _timer; public string SearchText get; set; [Parameter] public EventCallback<string> SearchCallback get; set; private async Task OnSearch() if (_timer != null) _timer.Dispose(); _timer = new Timer(async (sender) => await InvokeAsync(() => SearchCallback.InvokeAsync(SearchText); ); _timer.Dispose(); , null, 500, 0); await Task.CompletedTask;
参考
- 学习ASP.NET Core Blazor编程系列四——迁移 - DotNet菜园
- net::ERR_CERT_AUTHORITY_INVALID 在 ASP.NET Core 中
- ASP.NET内核中的net::ERR_CERT_AUTHORITY_INVALID - 问答 - 腾讯云开发者社区-腾讯云
- MariaDB + Visual Studio 2017 环境下的 ODBC 入门开发
- 文件上传参考:Upload files using InputFile component in Blazor,上传进度参考:c# web api post with progress reporting
- 增删改查参考:Asp.Net Core Blazor 开发体验
- fix to OnInitializedAsync being called twice in Blazor server project
- Blazor Navigation: Update URL without changing reloading page
开源项目参考:
- CodeMazeBlog/blazor-series at blazor-file-upload
- ErikXu/kubernetes-filesystem: System to browse, upload or download files for pods or containers in kubernetes.
- WebApiClient: An open source project based on the HttpClient. You only need to define the c# interface and modify the related features to invoke the client library of the remote http interface asynchronously.
- 原文 — 朱皮特的烂笔头
以上是关于C#Web开发之blazor体验的主要内容,如果未能解决你的问题,请参考以下文章