资讯详情

资讯详情

菜谱控制台应用前后端分离改造:基于 ASP.NET Core Minimal API 与 IHttpClientFactory 的 JSON REST 实战

示例工程【免费下载链接】practical-aspnetcorePractical samples of ASP.NET Core 11, 10, 9, 8.0, 7.0, 6.0, 5.0, 3.1, 2.2, and 2.1,projects you can use. Readme contains explanations on all projects.项目地址https://gitcode.com/gh_mirrors/pr/practical-aspnetcore点击查看免费下载本篇技术指南围绕 practical-aspnetcore 仓库入门路径Pathway 1中的第二个练习展开把上一个练习中创建的菜谱Recipe控制台应用拆分为「REST API 后端 控制台客户端」两个独立程序。你将掌握如何使用 ASP.NET Core Minimal API 搭建 JSON REST 服务、如何让后端统一承载 JSON 读写与业务逻辑以及如何用IHttpClientFactory让控制台通过 HTTP 与后端通信——这正是从单机程序迈向典型前后端分离架构的关键一步。练习目标与架构定位本练习exercises/pathway-1/exercise-2.md明确要求Take the recipe console application you have created and split it into two, one for backend with REST API and another is for the console.也就是说你需要在 exercise-1 完成的「单机菜谱管理控制台」基础之上做一次架构层面的拆分而不是重新实现业务。拆分后的形态是后端Backend一个 Web API 程序负责所有业务逻辑包括 JSON 文件的读取/写入、新建菜谱等控制台Console一个客户端程序本身不再直接触碰数据文件只通过HTTP 调用后端暴露的REST API完成操作通信协议前后端之间统一使用JSON传递数据。这一拆分蕴含了三个重要工程观念关注点分离数据持久化、业务规则集中放在服务端客户端只负责发起请求、呈现结果接口契约化控制台与后端之间以 REST 端点 JSON 载荷为契约任何一端都可以独立演进、替换甚至换语言重写真实 HTTP 语义练习要求你「先读透 HTTP 协议、理解如何设计 Web API」因为 CRUD 操作列出、新增、编辑菜谱会映射为GET、POST、PUT等不同的 HTTP 动词。仓库中与本练习直接相关的资源有两组正是后面两个章节的素材来源projects/httpclientfactory控制台侧的 HTTP 客户端方案与 projects/minimal-api后端 Web API 的最简写法。后端用 Minimal API 搭建菜谱 REST 服务练习允许使用 ASP.NET Core 6 的 Minimal APIWebApplication风格来搭建后端。仓库 minimal-api/hello-world 展示了最精简的入口形态using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; WebApplication app WebApplication.Create(); app.Run(async context { await context.Response.WriteAsync(Hello world .NET 6. Make sure you run this app using dotnet watch run.); }); await app.RunAsync();端点映射从 Map 到 MapGet / MapPostMinimal API 的核心是把「HTTP 端点」直接映射到方法。仓库 minimal-api/map 给出了最朴素的写法IResult Index() Results.Json(new Greeting(Hello World)); IResult About() Results.Json(new { about me }); var app WebApplication.Create(); app.Map(/, Index); app.Map(/about, About); app.Run(); public record Greeting(string Message);注意这里出现了两个贯穿全文的关键元素Results.Json(...)把对象序列化为 JSON 响应这正是「JSON 通信」在后端侧的出口IResultMinimal API 的统一返回类型让端点可以灵活返回 JSON、状态码等内容。对于菜谱场景建议按 HTTP 动词精确映射使用MapGet/MapPost/MapPut仓库 minimal-api/map-post 展示了MapPost配合 JSON 模型绑定的完整写法IResult Greet([FromBody] Greeting greet) Results.Json(new { greet.Message }); var app WebApplication.Create(); app.MapPost(/greet, Greet); app.Run(); public record Greeting(string Message);把这段代码套到菜谱模型上一个最小可运行的后端骨架就是using Microsoft.AspNetCore.Mvc; var app WebApplication.Create(); // 菜谱数据模型继承自 exercise-1 的定义 public record Recipe(string Title, string[] Ingredients, string Instructions, string[] Categories); // 存储与业务逻辑统一收拢到后端 var recipes new ListRecipe(); app.MapGet(/recipes, () Results.Json(recipes)); // 列出全部菜谱 app.MapGet(/recipes/{title}, (string title) recipes.FirstOrDefault(r r.Title title) is { } r ? Results.Json(r) : Results.NotFound()); // 按标题查找 app.MapPost(/recipes, ([FromBody] Recipe recipe) { recipes.Add(recipe); return Results.Created($/recipes/{recipe.Title}, recipe); // 新建菜谱 }); app.Run();业务逻辑与 JSON 持久化收归后端练习反复强调一个原则Backend handles all the business logic including the JSON reading/writing, creating a new recipe, etc.也就是 JSON 文件的读写、菜谱的新增与编辑都必须发生在后端进程中控制台不再碰磁盘文件。持久化层可以直接使用System.Text.Jsonexercise-1 中已经要求掌握把ListRecipe序列化写入磁盘、启动时读回内存例如using System.Text.Json; var file Path.Combine(AppContext.BaseDirectory, recipes.json); // 启动时读取 ListRecipe recipes File.Exists(file) ? JsonSerializer.DeserializeListRecipe(await File.ReadAllTextAsync(file)) ?? [] : []; // 每次变更后落盘 await File.WriteAllTextAsync(file, JsonSerializer.Serialize(recipes, new JsonSerializerOptions { WriteIndented true }));仓库 projects/json/json/Program.cs 展示了Results.Json序列化复杂对象含字典、可空值、DateTimeOffset的行为可以作为设计菜谱 JSON 载荷时的参考。用 MapGroup 组织端点当端点增多时可以用MapGroup统一前缀并抽取扩展方法见仓库 minimal-api/map-group-1app.MapGroup(/recipes).MapRecipesApi(); public static class RecipesApi { public static RouteGroupBuilder MapRecipesApi(this RouteGroupBuilder group) { group.MapGet(/, () Results.Ok(all recipes)); group.MapGet(/{title}, (string title) Results.Ok(title)); return group; } }这样GET /recipes/、GET /recipes/{title}等路由都被归并到同一前缀下代码结构更清晰。控制台通过 HttpClient 与后端对话后端就绪后控制台程序的职责变成「发起 HTTP 请求、解析 JSON 响应、渲染结果」。练习给出的关键资源是 projects/httpclientfactory 下的四个示例它们覆盖了从「最基础用法」到「类型化客户端」的完整演进路线。第一步IHttpClientFactory 集中管理 HttpClientHttpClient是 .NET 中发起 HTTP 请求的核心类型但直接new HttpClient()并长期持有会带来连接复用与 DNS 变更等问题。IHttpClientFactory正是为解决这些问题而生的集中管理方案。仓库 httpclientfactory-1 展示了最基础的接入方式var builder WebApplication.CreateBuilder(); builder.Services.AddHttpClient(); // 注册 IHttpClientFactory 服务 var app builder.Build(); app.Run(async context { var httpClient context.RequestServices.GetServiceIHttpClientFactory(); var client httpClient.CreateClient(); // 每次创建一个受管理的 HttpClient var result await client.GetStringAsync(http://scripting.com/rss.xml); context.Response.Headers.Append(Content-Type, application/rssxml); await context.Response.WriteAsync(result); }); app.Run();该示例在 Web 应用中演示但AddHttpClient()CreateClient()的模式对控制台同样适用——在控制台程序中你只需先Host.CreateApplicationBuilder或WebApplication.CreateBuilder注册服务再从 DI 容器取出IHttpClientFactory使用。第二步命名客户端预配置请求头仓库 httpclientfactory-2 展示了「命名客户端」给客户端起一个名字并集中配置默认请求头等参数var builder WebApplication.CreateBuilder(); builder.Services.AddHttpClient(rss, c { // Configure your http client here c.DefaultRequestHeaders.Add(Accept, application/rssxml); }); var app builder.Build(); app.Run(async context { var httpClient context.RequestServices.GetServiceIHttpClientFactory(); var client httpClient.CreateClient(rss); // 使用预配置的命名客户端 var result await client.GetStringAsync(http://scripting.com/rss.xml); ... });对于菜谱后端你可以定义一个名为recipes的命名客户端统一设置BaseAddress http://localhost:5000与Accept: application/json之后所有请求都从这一个配置源派生避免在每处调用点重复写 URL 前缀。第三步类型化客户端把 HTTP 封装成服务命名客户端仍需要你手写CreateClient与 URL 拼接。仓库 httpclientfactory-3 展示了更面向对象的方式——类型化客户端Typed Clientvar builder WebApplication.CreateBuilder(); builder.Services.AddTransientRssReader(); builder.Services.AddHttpClientRssReader(); // 由 DI 把 HttpClient 注入进 RssReader var app builder.Build(); app.Run(async context { var rss context.RequestServices.GetServiceRssReader(); var result await rss.Get(http://scripting.com/rss.xml); ... }); public class RssReader { readonly HttpClient _client; public RssReader(HttpClient client) { _client client; } public Taskstring Get(string url) _client.GetStringAsync(url); }把它映射到菜谱场景创建一个RecipeService把GetAllRecipes()、CreateRecipe(Recipe)等 API 调用全部收进类内控制台的主流程只面向RecipeService编程完全不感知 HTTP 细节public class RecipeService { private readonly HttpClient _client; public RecipeService(HttpClient client) { _client client; _client.BaseAddress new Uri(http://localhost:5000); } public async TaskListRecipe GetAllAsync() await _client.GetFromJsonAsyncListRecipe(/recipes) ?? []; public async TaskRecipe? CreateAsync(Recipe recipe) { var response await _client.PostAsJsonAsync(/recipes, recipe); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsyncRecipe(); } }这里用到的GetFromJsonAsync/PostAsJsonAsync/ReadFromJsonAsync是System.Net.Http.Json提供的扩展方法会自动完成 JSON 序列化与反序列化正好对上练习「用 JSON 通信」的要求。第四步接口化解耦实现仓库 httpclientfactory-4 进一步把类型化客户端背后的实现类替换为接口注入builder.Services.AddTransientRssReader(); builder.Services.AddHttpClientIRss, RssReader(); // 向接口注入实现 public interface IRss { Taskstring Get(string url); } public class RssReader : IRss { readonly HttpClient _client; public RssReader(HttpClient client) _client client; public Taskstring Get(string url) _client.GetStringAsync(url); }对应菜谱控制台你可以定义IRecipeService接口再让RecipeService实现它。这样控制台程序依赖的是抽象接口后端地址变更、通信方式调整时只需改动一处实现也便于未来编写针对IRecipeService的单元测试仓库 testing/nunit-1 展示了 .NET 测试工程的组织方式。JSON 数据契约菜谱模型如何在前后端间流动「Use JSON to communicate data between the backend and the console」是本练习的硬性要求JSON 是整个系统的数据契约。来自 exercise-1 的菜谱实体定义如下Title标题Ingredients配料Instructions做法说明Categories一个或多个分类在前后端各写一份结构完全一致的 C# 类型如record Recipe(string Title, string[] Ingredients, string Instructions, string[] Categories)即可保证序列化后的 JSON 形状一致。后端返回的典型载荷形如{ title: Nasi Goreng, ingredients: [rice, egg, soy sauce], instructions: Fry the rice with egg and soy sauce., categories: [indonesian, main] }需要留意两侧的契约一致性Ingredients与Categories用数组表达「多值」Instructions用字符串表达「长文本」前后端必须保持相同的属性名或统一配置JsonSerializerOptions的命名策略否则反序列化会得到空对象。HTTP 语义与 REST API 设计要点练习特别强调「Make sure you have read and understood how HTTP protocol works and how to design Web APIs」。把菜谱的增删改查映射为 REST 资源操作是最直接的落地方式操作HTTP 方法路径语义列出全部菜谱GET/recipes幂等读取返回 JSON 数组查看单个菜谱GET/recipes/{title}按标识读取未找到返回404新建菜谱POST/recipes请求体携带菜谱 JSON创建后返回201 Created编辑菜谱PUT/recipes/{title}请求体携带更新后的完整菜谱管理分类GET/POST/PUT/categories分类作为独立资源维护设计时把握三条原则动词交给 HTTP路径只描述资源不要出现/recipes/create、/recipes/delete这类把动作写进路径的 URL新建用POST、删除用DELETE用状态码表达结果成功创建返回201 Created对应Results.Created(...)找不到资源返回404对应Results.NotFound()校验失败返回400仓库 minimal-api/map 与 minimal-api/map-post 分别演示了Results.Json、Results.Ok、Results.Created的典型用法载荷与模型解耦客户端发送的 JSON 由[FromBody]绑定为Recipe对象见 minimal-api/map-post-2 的静态方法写法后端校验通过后才落盘。运行、验证与排错推荐的联调方式是开两个终端# 终端一启动后端 cd projects/minimal-api/map-post # 或你自己的 Recipe.Api 项目 dotnet run # 也可用 dotnet watch run 热重载 # 终端二运行控制台客户端 cd path/to/your/Recipe.Console dotnet run验证路径有三层后端自测用浏览器或curl直接访问端点确认GET /recipes返回 JSON、POST /recipes返回201控制台自测运行控制台依次执行列出、新建、编辑操作确认终端输出的内容与后端 JSON 文件中的内容一致边界检查在控制台输入一个不存在的菜谱标题确认后端返回404且控制台能给出友好提示而非崩溃。常见问题速查连接被拒绝多为后端未启动或控制台里BaseAddress端口写错返回 JSON 但反序列化出空对象前后端属性名不一致检查JsonSerializerOptions命名策略是否统一POST 后数据没落盘确认写文件逻辑放在后端业务逻辑唯一归属且每次变更后都调用序列化写入请求头不匹配后端期望Content-Type: application/json客户端用PostAsJsonAsync时已自动设置手写HttpClient.PostAsync时需手动补充。延伸与后续练习的衔接本练习是整个 Pathway 1 的关键枢纽后续练习全部建立在「JSON 后端」之上exercise-3保留 JSON 后端新增基于 Razor Pages 的 HTML 界面仍通过 HTTP Client 连接后端exercise-4后端调整 CORS 设置前端改用 Fetch API Alpine.js 的单页界面exercise-5把 JSON 后端替换为 gRPC 后端exercise-6为后端加入 JWT 认证并实现令牌刷新。也就是说只要在本练习中把「后端提供干净 REST API、客户端只走 HTTP JSON」的边界划清楚后续所有 UI 形态Razor Pages、SPA、gRPC、JWT都只是在替换这个边界的某一侧实现。本练习产出的Recipe数据模型与 REST 端点设计将作为整条学习路径的公共地基被反复复用。小结本练习的核心交付物是一份可运行的「REST 后端 控制台客户端」两份程序后端用 ASP.NET Core Minimal APIMapGet/MapPost/Results.Json参考 projects/minimal-api承载全部业务与 JSON 持久化控制台通过IHttpClientFactory参考 projects/httpclientfactory 的四个递进示例从基础CreateClient到命名客户端、类型化客户端、接口化注入发起 HTTP 调用并解析 JSON 响应。完成它你就完成了从「单体控制台程序」到「前后端分离的分布式雏形」的第一次真实跨越。赞分享示例工程【免费下载链接】practical-aspnetcorePractical samples of ASP.NET Core 11, 10, 9, 8.0, 7.0, 6.0, 5.0, 3.1, 2.2, and 2.1,projects you can use. Readme contains explanations on all projects.项目地址https://gitcode.com/gh_mirrors/pr/practical-aspnetcore点击查看免费下载相关推荐Stable Diffusion社区精选案例探索文本到图像生成技术的无限创意可能Stable Diffusion社区精选案例探索文本到图像生成技术的无限创意可能 Stable Diffusion作为一款强大的文本到图像生成工具正在全球范使用 ASP.NET Core 与 SQL Server FOR JSON 构建 ReactJS 评论应用sql-server-samples 中的 JSON 前后端集成实战使用 ASP.NET Core 与 SQL Server FOR JSON 构建 ReactJS 评论应用sql server samples 中的 JSON示例工程数据库教程后端如何轻松安装Linux Wacom驱动让你的数位板在Linux系统完美工作✨如何轻松安装Linux Wacom驱动让你的数位板在Linux系统完美工作✨ 如果你是Linux用户同时拥有Wacom数位板那么 Linux Wacom驱低代码前端上一篇TrollInstallerXiOS 14-16.6.1 TrollStore安装神器的高效部署指南下一篇三维场景智能对齐Scan2CAD实现扫描数据与CAD模型精准匹配的7个核心方法创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →