服务端使用ASP.NET Core SignalR与Vue3(TypeScript与JavaScript)前端建立通信(以进度条为例)

发布于:2022-12-11 ⋅ 阅读:(737) ⋅ 点赞:(0)

1. ASP.NET Core 

       ASP.NET Core是一个跨平台、高性能及开源的框架,用于生成基于云且连接互联网的新式应用程式。

官方文档:ASP.NET documentation | Microsoft Learn

 2. ASP.NET Core SignalR

       ASP.NET Core SignalR是开源库,用于服务端与客户端建立实时通信,可以自动管理连接;SignalR支持WebSockets、服务器发送的事件和长轮询。

官方文档:ASP.NET Core SignalR JavaScript 客户端 | Microsoft Learn

3. 编译环境 

       本文编译软件使用Visual Studio 2022与VS Code,服务端使用.NET6框架和C#编程语言,客户端使用Vue3框架、Vue-CLI和TypeScript编程语言以及Element UI组件。

4. 服务端代码编写

        创建基于C#的ASP.NET Core Web API项目,命名为“core-server”。

        在“Program.cs”文件里编写如下代码:

using core_server.Hubs;
using core_server.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers(); // 添加控制器
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// 引用SignalR
builder.Services.AddSignalR();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// 使用跨域
app.UseCors(x => x.SetIsOriginAllowed(_ => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);

app.UseHttpsRedirection();


app.UseAuthentication();
app.UseAuthorization();
app.UseDefaultFiles();
app.UseStaticFiles(); 
app.UseRouting(); // 路由中间件

// 将路径请求传入到SingalR.Hub类型中
// 泛型中的名字对应于Hubs文件夹下的类文件的名字
app.MapHub<ProgressHub>("/progressHub"); 

app.MapControllers(); // 路由配置
app.Run();

        在“core-server”下新建“Hubs”文件夹,在其下新建“ProgressHub.cs”,代码如下:

using Microsoft.AspNetCore.SignalR;

namespace core_server.Hubs
{
    public class ProgressHub: Hub
    {
        // 每个客户端连接后, 都会触发此方法
        public override async Task OnConnectedAsync()
        {
            await Clients.Client(Context.ConnectionId).SendAsync("SetHubConnId", Context.ConnectionId);

            await base.OnConnectedAsync();
        }
    }
}

       在“core-server”下的“Controllers”控制器文件夹下新建“ProgressControler.cs”文件,代码如下:

using Microsoft.AspNetCore.Mvc;
using core_server.Hubs;
using core_server.Models;
using Microsoft.AspNetCore.SignalR;
using System.Diagnostics;
using System;

namespace core_server.Controllers
{
    [ApiController]
    [Route("api/progress")] // 路由
    public class ProgressController: Controller
    {
        private readonly ILogger<ProgressController> _logger;
        private readonly IHubContext<ProgressHub> progressHubContext;

        public ProgressController(ILogger<ProgressController> logger, IHubContext<ProgressHub> _hubContext)
        {
             _logger = logger;
            progressHubContext = _hubContext;
        }

        [HttpPost]
        public async Task<IActionResult> Port(Dictionary<string, string> inModel)
        {
            Dictionary<string, string> outModel = new Dictionary<string, string>();
            int progress = 0;
            for (int i = 1; i <= 10; i++)
            {
                Thread.Sleep(500); 
                progress = i * 10;
                // UpdProgress用于客户端请求的方法或事件
                await progressHubContext.Clients.All.SendAsync("UpdProgress", progress);
            }
            outModel["ResultMsg"] = "上传图片完成,正在处理中...";
            outModel["status"] = "200";
            return Json(outModel);
        }

        [HttpGet]
        public async Task<IActionResult> Port1()
        {
            int progress = 0;
  
            for (int i = 1; i <= 10; i++)
            {
                Thread.Sleep(1000); // 暂停1秒
                progress = i * 10;
                await progressHubContext.Clients.All.SendAsync("HandleProgress", progress);
            }
            return Accepted(1);
        }
    }
}

        至此服务端代码书写完毕,项目文件夹:

          启动运行,浏览器弹出Swagger UI界面,可以进行API测试。

 5. 前端代码编写

        创建Vue脚手架项目,使用TypeScript,在JavaScript中亦可,安装signalR和axios。

npm install @microsoft/signalr

npm install axios // yarn add axios

     在“main.ts”中配置axios。

import axios from "axios";
axios.defaults.baseURL = "https://localhost:7103/"; // 服务端地址

// 全局挂载axios
app.config.globalProperties.$http = axios;

     在“views“文件夹里创建HomeView.vue,代码如下 :

<template>
  <el-container class="map-container">
    <el-header height="45px">
      <el-row :gutter="20">
        <el-col :span="4"><img class="logo" src="../assets/logo.png" /></el-col>
        <el-col :span="16"><h2>测试进度条</h2></el-col>
        <el-col :span="4"></el-col>
      </el-row>
    </el-header>
    <el-container>
      <el-container class="main-container">
        <el-main>
          <el-steps :active="1" align-center space="420px">
            <el-step title="上传图片" :icon="Edit" />
            <el-step title="处理中..." :icon="Upload" />
            <el-step title="处理完成" :icon="Picture" />
          </el-steps>
          <div class="demo-progress">
            <el-progress
              type="circle"
              :percentage="progressUpload"
              :status="finishedUpload"
            />
            <el-progress
              type="circle"
              :percentage="progressHandle"
              :status="finishedHandle"
            />
            <el-progress type="circle" :percentage="100" status="success" />
          </div>
          <div class="progress">
            <el-button type="primary" @click="StartProgress"
              >开始执行</el-button
            >
            <br />
            <el-tag v-if="isShow" effect="dark" type="success" class="result"
              >执行结果: {{ message }}</el-tag
            >
          </div>
        </el-main>
      </el-container>
    </el-container>
  </el-container>
</template>
<script lang="ts">
import { ref, defineComponent, reactive, getCurrentInstance } from "vue";
import { ElMessage } from "element-plus";
import { Edit, Picture, Upload } from "@element-plus/icons-vue";
import * as signalR from "@microsoft/signalr";

export default defineComponent({
  name: "HomeView",
  setup() {
    let progressUpload = ref(0);
    let progressHandle = ref(0);

    let message = ref("");
    let isShow = ref(false);
    let hub = reactive({
      connection: {},
      HubConnId: "",
      resultInfo: {},
    });
    let finishedUpload = ref("");
    let finishedHandle = ref("");
    // #region
    // 与服务器建立连接
    const connectionServer = (hub.connection =
      new signalR.HubConnectionBuilder()
        .withUrl("https://localhost:7103/progressHub")
        .build());
    connectionServer
      .start()
      .then(() => {
        ElMessage({
          message: "服务器连接成功了",
          type: "success",
        });
      })
      .catch((error) => {
        ElMessage.error(`服务器连接失败了\r${error.message.toString()}`);
      });
    connectionServer.onclose((error) => {
      ElMessage.error(`服务器疑似断开了\r${error?.message.toString()}`);
    });

    connectionServer.on("SetHubConnId", (id) => {
      hub.HubConnId = id;
    });
    connectionServer.on("UpdProgress", (percent) => {
      progressUpload.value = percent;
    });

    connectionServer.on("HandleProgress", (percent) => {
      progressHandle.value = percent;
    });
    // 使用axios
    const internalInstance = getCurrentInstance();
    const axios = internalInstance?.appContext.config.globalProperties.$http;

    async function StartProgress() {
      ElMessage({
        message: "开始上传图片",
        type: "success",
      });
      isShow.value = true;

      const postData = {};
      postData["HubConnId"] = hub.HubConnId;
      progressUpload.value = 0;
      progressHandle.value = 0;
      finishedUpload.value = "";
      finishedHandle.value = "";
      message.value = "正在执行中...";

      const { data: res } = await axios.post("api/progress", hub.resultInfo);
      if (res.status !== 200 + "") {
        finishedUpload.value = "exception";
        return ElMessage.error("上传图片失败了, 请重试");
      }
      finishedUpload.value = "success";
      message.value = res.ResultMsg;
      const res1 = await axios.get("api/progress");
      console.log(res1);
      if (res1.status !== 202) {
        finishedHandle.value = "exception";
        return ElMessage.error("图片处理失败了, 请重试");
      }
      finishedHandle.value = "success";
      message.value = "处理已完成";
    }
    // #endregion

    return {
      progressUpload,
      progressHandle,
      message,
      isShow,
      hub,
      finishedUpload,
      finishedHandle,
      Edit,
      Picture,
      Upload,
      StartProgress,
    };
  },
});
</script>
<style lang="scss" scoped>
.map-container {
  height: 100%;
}
.logo {
  height: 40px;
}
h2 {
  margin-left: 12px;
  letter-spacing: 0.06em;
  color: #fff;
  font-size: 20px;
  height: 40px;
  line-height: 40px;
}
.el-header {
  background-color: rgb(26, 54, 82);
  display: flex;
  justify-content: space-between;
  padding-left: 0;
  font-size: 22px;
  align-items: center;
}
.main-container {
  background-color: #eee;
}
.demo-progress {
  display: flex;
  justify-content: center;
  margin-left: 150px;
}
.demo-progress .el-progress--line {
  margin-bottom: 15px;
  width: 500px;
}
.demo-progress .el-progress--circle {
  margin-right: 288px;
}
.el-steps {
  padding-bottom: 20px;
}
:deep(.el-step__icon.is-icon) {
  width: 40px;
  background-color: #eee;
}
.progress {
  margin: 3% 12%;

  .result {
    margin-top: 20px;
    font-size: 14px;
  }
}
</style>

        启动运行:


网站公告

今日签到

点亮在社区的每一天
去签到