惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

www.infosecurity-magazine.com
www.infosecurity-magazine.com
D
DataBreaches.Net
T
Tailwind CSS Blog
M
MIT News - Artificial intelligence
Stack Overflow Blog
Stack Overflow Blog
F
Full Disclosure
V2EX - 技术
V2EX - 技术
N
News and Events Feed by Topic
Help Net Security
Help Net Security
L
LangChain Blog
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
Google Online Security Blog
Google Online Security Blog
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Blog of Author Tim Ferriss
Google DeepMind News
Google DeepMind News
The Register - Security
The Register - Security
B
Blog RSS Feed
N
Netflix TechBlog - Medium
N
News | PayPal Newsroom
TaoSecurity Blog
TaoSecurity Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
V
Vulnerabilities – Threatpost
B
Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
I
Intezer
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园_首页
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
AI
AI
aimingoo的专栏
aimingoo的专栏
大猫的无限游戏
大猫的无限游戏
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cyberwarzone
Cyberwarzone
P
Proofpoint News Feed
Google DeepMind News
Google DeepMind News
G
GRAHAM CLULEY
Vercel News
Vercel News
罗磊的独立博客
MyScale Blog
MyScale Blog
Last Week in AI
Last Week in AI
博客园 - 司徒正美
C
CERT Recently Published Vulnerability Notes
GbyAI
GbyAI
Scott Helme
Scott Helme
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Troy Hunt's Blog
A
About on SuperTechFans
P
Privacy International News Feed

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
.NET 8 Web开发入门(六):Blazor 全栈开发——告别 JavaScript 焦虑
码农刚子 · 2026-05-23 · via 博客园_首页

大家好,我是码农刚子。最近好几天没更了,刷课时、考试、兼职,搞得我眼睛的睁不开了,今天终于有时间更新一篇。也不知道有没有朋友在期待?

一、前言:后端开发的“前端痛”

在传统开发中,全栈往往意味着你要精通 C# 还要精通 JavaScript、HTML、CSS,甚至还要学 React 或 Vue 的一大堆生命周期和状态管理库。这对很多专注于业务逻辑的后端同学来说,简直是噩梦。

微软也意识到了这个痛点,于是 Blazor 诞生了。

Blazor 的核心理念很简单:用 C# 替代 JavaScript。你可以在 Razor 页面里写 C# 代码,处理点击事件,甚至直接操作 DOM(虽然不推荐)。这让 .NET 开发者拥有了无缝的全栈开发能力。

二、Blazor Server:实时交互的黑魔法

Blazor 主要有两种托管模型,新手最容易搞混,刚子先帮你理一理:

  1. Blazor WebAssembly (客户端):应用被编译成 WebAssembly,直接在浏览器里运行。这就像 React 一样,部署在静态服务器上,不依赖服务器实时计算。
  2. Blazor Server (服务端):应用在服务器上运行,通过 SignalR(一种 WebSocket 技术)与浏览器实时通信。

刚子敲黑板: 本教程选择 Blazor Server 作为入门。为什么?

  • 调试简单:你可以直接在 Visual Studio 里打断点,断点会停在后端的 C# 代码上,就像调试普通后台代码一样。
  • 加载快:浏览器不需要下载整个 .NET 运行时,首屏加载速度极快。
  • 适合后台管理系统:非常适合企业内部使用的后台管理界面开发。

三、动手实战:创建第一个 Blazor 项目

我们重新创建一个项目,为了方便演示,这次建立一个独立的 Blazor Server 项目。

3.1 创建项目

在终端执行:

dotnet new blazorserver -n MyTodoUI
cd MyTodoUI
dotnet run

可视化界面创建项目: 打开vs2022→点击创建新项目→选择Blazor Web 应用,点击下一步→依次输入项目名称、解决方案及项目文件路径,点击下一步→选择.NET 8 ,点击创建即可

点击创建新项目 选择Blazor Web 应用 依次输入项目名称、解决方案及项目文件路径 创建.NET 8项目

启动后,浏览器访问 https://localhost:xxxx,你会看到一个漂亮的官方模板页面。

Blazor Server 应用程序默认模板首页展示图。

注意:这里创建应用时要选择 Blazor Web 应用 而不是 Blazor Server 应用。从.NET 8开始,经典的“Blazor Server”模板已被全新的“Blazor Web App”模板所取代,这一点在微软的官方文档和技术社区中都有提及。新的模板统一了Blazor Server和Blazor WebAssembly的开发体验,并引入了更灵活的“交互渲染模式”(Interactive Render Modes),因此VS不再直接提供名为“Blazor Server”的.NET 8项目选项。创建Blazor Server 应用不支持.NET 8。

3.2 项目结构解析

打开项目,重点关注以下几个文件夹:

blazor web 应用 结构图

  • Layout/:存放公共布局组件,如 MainLayout.razor(布局模板)和 NavMenu.razor(导航菜单)。
  • Pages/:存放 .razor 页面组件。比如 Home.razor 就是首页。
  • _Imports.razor:类似于 using 命名空间,这里引入的命名空间会自动应用到所有 Razor 组件。

四、组件化开发:一切皆组件

Blazor 的开发模式是组件化的。一个页面通常由多个小组件拼凑而成。

4.1 创建一个简单的计数器组件

打开 Pages/Home.razor,把内容清空,写入以下代码:

@page "/" <!-- 路由:访问根路径时显示这个组件 -->
@rendermode InteractiveServer

<h1>欢迎来到刚子的计数器</h1>

<p>当前计数: <strong>@currentCount</strong></p>

<button class="btn btn-primary" @onclick="IncrementCount">点击我 +1</button>

@code {
    // 这里写 C# 逻辑
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

代码解析

  • @page "/":定义路由,告诉 Blazor 这是首页。
  • @currentCount:使用 @ 符号将 C# 变量输出到 HTML 中。
  • @onclick="IncrementCount":将 HTML 的点击事件绑定到 C# 的方法上。

运行项目,点击按钮,你会发现数字变了!而且完全不需要写 JavaScript,也不用操作 DOM(document.getElementById...),Blazor 会自动帮你更新界面。

五、实战进阶:连接后端 API

光有个计数器没意思,我们要把之前教程里做好的 Todo API 接入进来。

为了模拟真实的前后端分离场景,我们假设后端 API 运行在 http://localhost:5000

快速新建一个极简的 ASP.NET Core Web API

不需要手动写控制器,直接用 最小 API 方式,几行代码就搞定。步骤如下:

  1. 在现有解决方案中添加新项目

    右键解决方案 → 添加 → 新建项目

    选择 ASP.NET Core Web API

    项目名称:TodoApi

    框架:.NET 8

    不勾选“使用控制器” (这样会生成最小 API 模板)

  1. 修改 Program.cs 为以下代码
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// 内存数据存储
var todos = new List<Todo>
{
    new Todo { Id = 1, Title = "学习 Blazor", IsDone = false },
    new Todo { Id = 2, Title = "测试 API", IsDone = true }
};


// GET /todos
app.MapGet("/todos", () => todos);

// DELETE /todos/{id}
app.MapDelete("/todos/{id}", (int id) =>
{
    var todo = todos.FirstOrDefault(t => t.Id == id);
    if (todo == null) return Results.NotFound();
    todos.Remove(todo);
    return Results.NoContent();
});

// POST /todos
app.MapPost("/todos", (Todo todo) =>
{
    todo.Id = todos.Max(t => t.Id) + 1;
    todos.Add(todo);
    return Results.Created($"/todos/{todo.Id}", todo);
});

app.Run();

public class Todo
{
    public int Id { get; set; }
    public string? Title { get; set; }
    public bool IsDone { get; set; }
}

注意路由是 /todos(复数),没有 api 前缀。后面的前端调用也需要同步修改。

5.1 注入 HttpClient

在 Blazor Web App 中,我们通过 HttpClient 发起网络请求。首先在 Program.cs 中注册服务:

// Program.cs

// ... 其他代码 ...

// 注册 HttpClient,BaseAddress 设为后端 API 地址
builder.Services.AddScoped(sp => new HttpClient 
{ 
    BaseAddress = new Uri("http://localhost:5000") // 这里替换为你实际的后端地址
});

// ...

5.2 定义数据模型

为了接收 JSON 数据,我们需要定义一个简单的 DTO 类。在项目中新建 Models/TodoItemDto.cs

public class TodoItemDto
{
    public int Id { get; set; }
    public string? Title { get; set; }
    public bool IsDone { get; set; }
}

5.3 获取并展示数据

新建一个页面 Pages/Todo.razor

@page "/todo"
@rendermode InteractiveServer
@using MyTodoUI.Models
@inject HttpClient Http 

<h1>待办事项列表</h1>

@if (todos == null)
{
    <p><em>正在加载数据...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>标题</th>
                <th>状态</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in todos)
            {
                <tr>
                    <td>@item.Id</td>
                    <td>@item.Title</td>
                    <td>@(item.IsDone ? "已完成" : "进行中")</td>
                    <td>
                        <button class="btn btn-sm btn-danger" @onclick="() => DeleteTodo(item.Id)">删除</button>
                    </td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private List<TodoItemDto>? todos;

    // 组件初始化时触发,适合加载数据
    protected override async Task OnInitializedAsync()
    {
        try 
        {
            // 调用 GET /todos 接口
            todos = await Http.GetFromJsonAsync<List<TodoItemDto>>("todos");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"获取数据失败: {ex.Message}");
        }
    }

    private async Task DeleteTodo(int id)
    {
        // 调用 DELETE /todos/{id} 接口
        await Http.DeleteAsync($"todos/{id}");
        
        // 刷新列表
        todos = await Http.GetFromJsonAsync<List<TodoItemDto>>("todos");
    }
}

Blazor 页面展示从后端 API 获取的待办事项列表数据截图

刚子小贴士: 注意到 @inject HttpClient Http 了吗?这依然是依赖注入。Blazor 的组件本质上也是一个类,我们可以直接在视图中注入服务,非常方便。

另外 Blazor Web App,务必在组件上添加 @rendermode InteractiveServer

OnInitializedAsync 是 Blazor 的生命周期方法,相当于 React 的 useEffect 或 Vue 的 mounted,在这里发起网络请求是最标准的做法。

六、双向绑定:让表单变简单

Web 开发离不开表单。Blazor 提供了强大的双向绑定功能。

6.1 添加新待办

我们在 Todo.razor 底部添加一个输入框:

<h3>添加新任务</h3>
<EditForm Model="@newTodo" OnValidSubmit="@AddTodo">
    <div class="form-group">
        <InputText @bind-Value="newTodo.Title" class="form-control" placeholder="输入任务内容" />
    </div>
    <button type="submit" class="btn btn-success mt-2">提交</button>
</EditForm>

@code {
    // ... 之前的代码 ...

   private TodoItemDto newTodo = new TodoItemDto() { };

	 private async Task AddTodo()
	 {
			 // 调用 POST /todos 接口
			 var response = await Http.PostAsJsonAsync("todos", newTodo);

			 if (response.IsSuccessStatusCode)
			 {
					 newTodo = new TodoItemDto(); // 清空输入框
					 todos = await Http.GetFromJsonAsync<List<TodoItemDto>>("todos"); // 刷新列表
			 }
	 }
}

核心解析

  • @bind="newTodo.Title":这是双向绑定的魔法。当你在输入框打字时,newTodo.Title 会自动更新;当你修改 newTodo 时,输入框也会自动变。
  • EditForm:Blazor 提供的表单组件,可以集成我们之前讲的 DataAnnotations 验证。

刚子敲黑板: Blazor 的表单处理比原生 HTML 强大得多。你甚至可以在 InputText 上加 <DataAnnotationsValidator />,直接复用我们在第五篇写的验证规则,前端连验证逻辑都不用写了!

七、延伸阅读:刚子的 Blazor 深度系列

本篇实战教程旨在帮你快速打通前后端。如果你在实战过程中,想要更深入地了解 Blazor 的基础概念、路由机制、布局系统或 JS 交互,强烈推荐阅读我之前整理的系列专栏,那里有更细致的图文讲解:

通过这些文章,你将建立起完整的 Blazor 知识体系。

八、总结与下篇预告

在这篇教程中,我们通过 Blazor Server 实现了一个简单但完整的“全栈”功能:

  1. 理解了 Blazor Server 的实时通信原理。
  2. 学会了使用 @inject 注入服务。
  3. 实现了从后端 API 获取数据、删除数据和提交数据。
  4. 掌握了双向绑定简化表单开发。

现在的你,已经具备了独立开发一个小型管理系统的能力:后端用 Minimal API + EF Core,前端用 Blazor Server,全部用 C# 贯通。

下一篇预告

我们的应用已经能跑能跳了,但在互联网上裸奔是很危险的。谁都可以调用你的 /todos 接口吗?显然不行。 在下一篇教程中,我们将引入 身份验证与授权。刚子将带你深入了解 JWT (JSON Web Token),给你的 API 加上“门禁卡”,只有拿着正确 Token 的用户才能进出。

安全防线,即将构筑!

如果本篇教程你对有用,欢迎点赞、收藏、转发给你身边的朋友,一起学习。

原文链接:.NET 8 Web开发入门(六):Blazor 全栈开发——告别 JavaScript 焦虑 - 码农刚子的开发笔记