基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(六)

系列文章

  1. 使用 abp cli 搭建项目

  2. 给项目瘦身,让它跑起来

  3. 完善与美化,Swagger登场

  4. 数据访问和代码优先

  5. 自定义仓储之增删改查

  6. 统一规范API,包装返回模型

  7. 再说Swagger,分组、描述、小绿锁

  8. 接入GitHub,用JWT保护你的API

  9. 异常处理和日志记录

  10. 使用Redis缓存数据

  11. 集成Hangfire实现定时任务处理

  12. 用AutoMapper搞定对象映射

  13. 定时任务最佳实战(一)

  14. 定时任务最佳实战(二)

  15. 定时任务最佳实战(三)

  16. 博客接口实战篇(一)

  17. 博客接口实战篇(二)

  18. 博客接口实战篇(三)

  19. 博客接口实战篇(四)

  20. 博客接口实战篇(五)

  21. Blazor实战系列(一)

  22. Blazor实战系列(二)

  23. Blazor实战系列(三)

  24. Blazor实战系列(四)

  25. Blazor实战系列(五)


上一篇完成了博客文章详情页面的数据展示和基于JWT方式的简单身份验证,本篇继续推进,完成后台分类管理的所有增删改查等功能。

分类管理

在 Admin 文件夹下新建Razor组件,Categories.razor,设置路由,@page "/admin/categories"。将具体的展示内容放在组件AdminLayout中。

@page "/admin/categories"<AdminLayout><Loading />
</AdminLayout>

在这里我会将所有分类展示出来,新增、更新、删除都会放在一个页面上去完成。

先将列表查出来,添加API的返回参数,private ServiceResult<IEnumerable<QueryCategoryForAdminDto>> categories;,然后再初始化中去获取数据。

//QueryCategoryForAdminDto.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{public class QueryCategoryForAdminDto : QueryCategoryDto{/// <summary>/// 主键/// </summary>public int Id { get; set; }}
}
/// <summary>
/// API返回的分类列表数据
/// </summary>
private ServiceResult<IEnumerable<QueryCategoryForAdminDto>> categories;/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{var token = await Common.GetStorageAsync("token");Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");categories = await FetchData();
}/// <summary>
/// 获取数据
/// </summary>
/// <returns></returns>
private async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> FetchData()
{return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>>("/blog/admin/categories");
}

初始化的时候,需要将我们存在localStorage中的token读取出来,因为我们后台的API都需要添加 Authorization Header 请求头才能成功返回数据。

在Blazor添加请求头也是比较方便的,直接Http.DefaultRequestHeaders.Add(...)即可,要注意的是 token值前面需要加 Bearer,跟了一个空格不可以省略。

获取数据单独提成了一个方法FetchData(),因为会频繁用到,现在在页面上将数据绑定进行展示。

@if (categories == null)
{<Loading />
}
else
{<div class="post-wrap categories"><h2 class="post-title">-&nbsp;Categories&nbsp;-</h2>@if (categories.Success && categories.Result.Any()){<div class="categories-card">@foreach (var item in categories.Result){<div class="card-item"><div class="categories"><NavLink title="❌删除" @onclick="@(async () => await DeleteAsync(item.Id))">❌</NavLink><NavLink title="????编辑" @onclick="@(() => ShowBox(item))">????</NavLink><NavLink target="_blank" href="@($"/category/{item.DisplayName}")"><h3>@item.CategoryName</h3><small>(@item.Count)</small></NavLink></div></div>}<div class="card-item"><div class="categories"><NavLink><h3 @onclick="@(() => ShowBox())">????~~~ 新增分类 ~~~????</h3></NavLink></div></div></div>}else{<ErrorTip />}</div>
}

同样的当categories还没成功获取到数据的时候,我们直接在展示 <Loading />组件。然后就是循环列表数据在foreach中进行绑定数据。

在每条数据最前面,加了删除和编辑两个按钮,删除的时候调用DeleteAsync方法,将当前分类的Id传给他即可。新增和编辑的时候调用ShowBox方法,他接受一个参数,当前循环到的分类对象item,即QueryCategoryForAdminDto

同时这里考虑到复用性,我写了一个弹窗组件,Box.Razor,放在Shared文件夹下面,可以先看一下标题为弹窗组件的内容再回来继续往下看。

删除分类

接下来看看删除方法。

/// <summary>
/// 删除分类
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private async Task DeleteAsync(int id)
{// 弹窗确认bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n????????真的要干掉这个该死的分类吗????????");if (confirmed){var response = await Http.DeleteAsync($"/blog/category?id={id}");var result = await response.Content.ReadFromJsonAsync<ServiceResult>();if (result.Success){categories = await FetchData();}}
}

删除之前搞个原生的confirm进行提示,避免手残误删。因为API那边使用的是HttpDelete,所有我们调用API时候要用Http.DeleteAsync,返回的是HttpResponseMessage对象,需要我们手动处理接收返回数据,将其转换为ServiceResult对象,如果判断删除成功后重新调用FetchData()刷新分类数据。

新增/更新分类

新增和更新数据选择使用弹窗的方式来进行(弹窗组件在下方),首先是需要一个参数判断弹窗是否打开,因为是将新增和更新放在一起,所以如何判断是新增还是更新呢?这里使用Id来进行判断,当编辑的时候肯定会有Id参数。新增的时候是没有参数传递的。

当我们打开弹窗后里面需要展示两个input框,用来供输入要保存的数据,同样是添加两个变量。

添加所需的这几个参数。

/// <summary>
/// 默认隐藏Box
/// </summary>
private bool Open { get; set; } = false;/// <summary>
/// 新增或者更新时候的分类字段值
/// </summary>
private string categoryName, displayName;/// <summary>
/// 更新分类的Id值
/// </summary>
private int id;

现在可以将Box组件添加到页面上。

<div class="post-wrap categories">...
</div><Box OnClickCallback="@SubmitAsync" Open="@Open"><div class="box-item"><b>DisplayName:</b><input type="text" @bind="@displayName" @bind:event="oninput" /></div><div class="box-item"><b>CategoryName:</b><input type="text" @bind="@categoryName" @bind:event="oninput" /></div>
</Box>

确定按钮回调事件执行SubmitAsync()方法,打开状态参数为上面添加的Open,按钮文字ButtonText为默认值不填。

添加了两个input,将两个分类字段分别绑定上去,使用@bind@bind:event。前者等价于设置其value值,后者等价于一个change事件当值改变后会重新赋给绑定的字段参数。

现在可以来看看点击了新增或者编辑按钮的方法ShowBox(...),接收一个参数QueryCategoryForAdminDto让其默认值为null。

/// <summary>
/// 显示box,绑定字段
/// </summary>
/// <param name="dto"></param>
private void ShowBox(QueryCategoryForAdminDto dto = null)
{Open = true;id = 0;// 新增if (dto == null){displayName = null;categoryName = null;}else // 更新{id = dto.Id;displayName = dto.DisplayName;categoryName = dto.CategoryName;}
}

执行ShowBox()方法,将弹窗打开,设置Open = true;和初始化id的值id = 0;

通过参数是否null进行判断是新增还是更新,这样打开弹窗就搞定了,剩下的就交给弹窗来处理了。

因为新增和更新API需要还对应的输入参数EditCategoryInput,去添加它不要忘了。

那么现在就只差按钮回调事件SubmitAsync()了,主要是给输入参数进行赋值调用API,执行新增或者更新即可。

/// <summary>
/// 确认按钮点击事件
/// </summary>
/// <returns></returns>
private async Task SubmitAsync()
{var input = new EditCategoryInput(){DisplayName = displayName.Trim(),CategoryName = categoryName.Trim()};if (string.IsNullOrEmpty(input.DisplayName) || string.IsNullOrEmpty(input.CategoryName)){return;}var responseMessage = new HttpResponseMessage();if (id > 0)responseMessage = await Http.PutAsJsonAsync($"/blog/category?id={id}", input);elseresponseMessage = await Http.PostAsJsonAsync("/blog/category", input);var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();if (result.Success){categories = await FetchData();Open = false;}
}

当参数为空时,直接return什么都不执行。通过当前Id判断是新增还是更新操作,调用不同的方法PutAsJsonAsyncPostAsJsonAsync去请求API,同样返回到是HttpResponseMessage对象,最后如果操作成功,重新请求一个数据,刷新分类列表,将弹窗关闭掉。

分类管理页面的全部代码如下:

@page "/admin/categories"<AdminLayout>@if (categories == null){<Loading />}else{<div class="post-wrap categories"><h2 class="post-title">-&nbsp;Categories&nbsp;-</h2>@if (categories.Success && categories.Result.Any()){<div class="categories-card">@foreach (var item in categories.Result){<div class="card-item"><div class="categories"><NavLink title="❌删除" @onclick="@(async () => await DeleteAsync(item.Id))">❌</NavLink><NavLink title="????编辑" @onclick="@(() => ShowBox(item))">????</NavLink><NavLink target="_blank" href="@($"/category/{item.DisplayName}")"><h3>@item.CategoryName</h3><small>(@item.Count)</small></NavLink></div></div>}<div class="card-item"><div class="categories"><NavLink><h3 @onclick="@(() => ShowBox())">????~~~ 新增分类 ~~~????</h3></NavLink></div></div></div>}else{<ErrorTip />}</div><Box OnClickCallback="@SubmitAsync" Open="@Open"><div class="box-item"><b>DisplayName:</b><input type="text" @bind="@displayName" @bind:event="oninput" /></div><div class="box-item"><b>CategoryName:</b><input type="text" @bind="@categoryName" @bind:event="oninput" /></div></Box>}
</AdminLayout>@code {/// <summary>/// 默认隐藏Box/// </summary>private bool Open { get; set; } = false;/// <summary>/// 新增或者更新时候的分类字段值/// </summary>private string categoryName, displayName;/// <summary>/// 更新分类的Id值/// </summary>private int id;/// <summary>/// API返回的分类列表数据/// </summary>private ServiceResult<IEnumerable<QueryCategoryForAdminDto>> categories;/// <summary>/// 初始化/// </summary>/// <returns></returns>protected override async Task OnInitializedAsync(){var token = await Common.GetStorageAsync("token");Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");categories = await FetchData();}/// <summary>/// 获取数据/// </summary>/// <returns></returns>private async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> FetchData(){return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>>("/blog/admin/categories");}/// <summary>/// 删除分类/// </summary>/// <param name="id"></param>/// <returns></returns>private async Task DeleteAsync(int id){Open = false;// 弹窗确认bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n????????真的要干掉这个该死的分类吗????????");if (confirmed){var response = await Http.DeleteAsync($"/blog/category?id={id}");var result = await response.Content.ReadFromJsonAsync<ServiceResult>();if (result.Success){categories = await FetchData();}}}/// <summary>/// 显示box,绑定字段/// </summary>/// <param name="dto"></param>private void ShowBox(QueryCategoryForAdminDto dto = null){Open = true;id = 0;// 新增if (dto == null){displayName = null;categoryName = null;}else // 更新{id = dto.Id;displayName = dto.DisplayName;categoryName = dto.CategoryName;}}/// <summary>/// 确认按钮点击事件/// </summary>/// <returns></returns>private async Task SubmitAsync(){var input = new EditCategoryInput(){DisplayName = displayName.Trim(),CategoryName = categoryName.Trim()};if (string.IsNullOrEmpty(input.DisplayName) || string.IsNullOrEmpty(input.CategoryName)){return;}var responseMessage = new HttpResponseMessage();if (id > 0)responseMessage = await Http.PutAsJsonAsync($"/blog/category?id={id}", input);elseresponseMessage = await Http.PostAsJsonAsync("/blog/category", input);var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();if (result.Success){categories = await FetchData();Open = false;}}
}

弹窗组件

考虑到新增和更新数据的时候需要弹窗,这里就简单演示一下写一个小组件。

在 Shared 文件夹下新建一个Box.razor

在开始之前分析一下弹窗组件所需的元素,弹窗肯定有一个确认和取消按钮,右上角需要有一个关闭按钮,关闭按钮和取消按钮一个意思。他还需要一个打开或者关闭的状态,判断是否打开弹窗,还有就是弹窗内需要自定义展示内容。

确定按钮的文字可以自定义,所以差不多就需要3个参数,组件内容RenderFragment ChildContent,是否打开弹窗bool Open默认隐藏,按钮文字string ButtonText默认值给"确定"。然后最重要的是确定按钮需要一个回调事件,EventCallback<MouseEventArgs> OnClickCallback 用于执行不同的事件。

/// <summary>
/// 组件内容
/// </summary>
[Parameter]
public RenderFragment ChildContent { get; set; }/// <summary>
/// 是否隐藏
/// </summary>
[Parameter]
public bool Open { get; set; } = true;/// <summary>
/// 按钮文字
/// </summary>
[Parameter]
public string ButtonText { get; set; } = "确定";/// <summary>
/// 确认按钮点击事件回调
/// </summary>
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }/// <summary>
/// 关闭Box
/// </summary>
private void Close() => Open = false;

右上角关闭和取消按钮直接在内部进行处理,执行Close()方法,将参数Open值设置为false即可。

对应的html如下。

@if (Open)
{<div class="shadow"></div><div class="box"><div class="close" @onclick="Close">❌</div><div class="box-content">@ChildContent<div class="box-item box-item-btn"><button class="box-btn" @onclick="OnClickCallback">@ButtonText</button><button class="box-btn btn-primary" @onclick="Close">取消</button></div></div></div>
}

关于样式

下面是弹窗组件所需的样式代码,大家需要的自取,也可以直接去GitHub实时获取最新的样式文件。

.box {width: 600px;height: 300px;border-radius: 5px;background-color: #fff;position: fixed;top: 50%;left: 50%;margin-top: -150px;margin-left: -300px;z-index: 997;
}
.close {position: absolute;right: 3px;top: 2px;cursor: pointer;
}
.shadow {width: 100%;height: 100%;position: fixed;left: 0;top: 0;z-index: 996;background-color: #000;opacity: 0.3;
}
.box-content {width: 90%;margin: 20px auto;
}
.box-item {margin-top: 10px;height: 30px;
}
.box-item b {width: 130px;display: inline-block;
}
.box-item input[type=text] {padding-left: 5px;width: 300px;height: 30px;
}
.box-item label {width: 100px;white-space: nowrap;
}
.box-item input[type=radio] {width: auto;height: auto;visibility: initial;display: initial;margin-right: 2px;
}
.box-item button {height: 30px;width: 100px;
}
.box-item-btn {position: absolute;right: 20px;bottom: 20px;
}
.box-btn {display: inline-block;height: 30px;line-height: 30px;padding: 0 18px;background-color: #5A9600;color: #fff;white-space: nowrap;text-align: center;font-size: 14px;border: none;border-radius: 2px;cursor: pointer;
}
button:focus {outline: 0;
}
.box-btn:hover {opacity: .8;filter: alpha(opacity=80);color: #fff;
}
.btn-primary {border: 1px solid #C9C9C9;background-color: #fff;color: #555;
}
.btn-primary:hover {border-color: #5A9600;color: #333;
}
.post-box {width: 98%;margin: 27px auto 0;
}
.post-box-item {width: 100%;height: 30px;margin-bottom: 5px;
}
.post-box-item input {width: 49.5%;height: 30px;padding-left: 5px;border: 1px solid #ddd;
}
.post-box-item input:nth-child(1) {float: left;margin-right: 1px;
}
.post-box-item input:nth-child(2) {float: right;margin-left: 1px;
}
.post-box .box-item b {width: auto;
}
.post-box .box-item input[type=text] {width: 90%;
}

好了,分类模块的功能都完成了,标签和友情链接的管理界面还会远吗?这两个模块的做法和分类是一样的,有兴趣的可以自己动手完成,今天到这吧,未完待续...

开源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[数据结构-严蔚敏版]P48栈的链式表示

代码如下: #include <iostream> using namespace std;typedef int ElemType;typedef struct SNode {ElemType data;SNode *next; }SNode;typedef struct {SNode *top; }SqStack;bool initStack(SqStack &s) {s.top nullptr;return true; }bool destroyStack(SqStac…

还有多少人不会用K8s?.NET高级高薪岗,开始要求了!

毫无疑问&#xff0c;Kubernetes已经成为容器编排事实标准。除了已经拥抱Kubernetes的Google、BAT、京东、奇虎360等巨头大厂外&#xff0c;更多的企业也都在向Kubernetes迁移。容器技术大势所趋&#xff0c;是互联网企业目前急需的技术人才之一&#xff0c;已成为运维工程师、…

[数据结构-严蔚敏版]P61ADT Queue的表示与实现(单链队列-队列的链式存储结构)

代码如下: #include <iostream> using namespace std;typedef int ElemType;typedef struct QNode {ElemType data;QNode *next; }QNode ,*QueuePtr;typedef struct {QueuePtr front;QueuePtr rear; }LinkQueue;bool initQueue(LinkQueue &q) {q.front q.rear new…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(七)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

[数据结构-严蔚敏版]P64循环队列-队列的顺序存储结构

代码如下: #include <iostream> using namespace std;const int MAXQSIZE 10;typedef int ElemType;typedef struct {ElemType *base;int front;int rear; }SqQueue;bool initQueue(SqQueue &q) {q.base new ElemType[MAXQSIZE];if (!q.base) return false;q.fron…

BeetleX.FastHttpApi之Vuejs扩展

非常喜欢用vuejs,但又不想花时间去搞nodejs和webpack之类的&#xff0c;所以才有了BeetleX.FastHttpApi.VueExtend这样一个组件&#xff1b;组件的主要功能就是可以实现在vs.net中编写*.vue并直接引用到服务中&#xff0c;这样对于我这个习惯在vs.net写服务应的带来极大的方便性…

C++的new、delete需要注意的一点:使用危险函数导致的越界CRT detected that the application wrote to memory after end of heap

new、delete需要注意的一个特性 正常情况new一个数组之后&#xff0c;用delete释放是没有问题的。但是当对new得到的堆区进行越界的写入操作&#xff08;读操作不会&#xff09;将会导致delete时出现段错误&#xff0c;无法进行删除。如下面的程序所示&#xff1a; 数组大小只…

数据结构与算法专题——第四题 字符串相似度

这篇我们看看 最长公共子序列 的另一个版本&#xff0c;求字符串相似度(编辑距离)&#xff0c;我也说过了&#xff0c;这是一个非常实用的算法&#xff0c;在DNA对比&#xff0c;网页聚类等方面都有用武之地。一&#xff1a;概念对于两个字符串 A 和 B&#xff0c;通过基本的增…

[数据结构-严蔚敏版]P71串的抽象数据类型的定义

代码如下: #include <iostream> #include <string> using namespace std;typedef struct {char *ch;int length; }String;bool initString(String &s) {s.ch nullptr;s.length 0;return true; }bool strAssign(String &s, const char *ch) {int len st…

三分钟学会.NET Core Jwt 策略授权认证

一.前言大家好我又回来了&#xff0c;前几天讲过一个关于Jwt的身份验证最简单的案例&#xff0c;但是功能还是不够强大&#xff0c;不适用于真正的项目&#xff0c;是的&#xff0c;在真正面对复杂而又苛刻的客户中&#xff0c;我们会不知所措&#xff0c;就现在需要将认证授权…

[数据结构-严蔚敏版]P65离散事件模拟(银行客户的离散事件驱动模拟程序)

写这个简单玩意&#xff0c;居然花费了我6小时&#xff0c;唉!!!&#xff0c;还是太菜了! 中间已经起了放弃的念头了&#xff0c;最后还是坚持下来了! 总结&#xff1a; (1)漏了p p->next (2)队列删除元素的时候&#xff0c;删除的是最后一个忘记特判。 (3)写的时候太急了…

[温故知新] 编程原则和模式

写了这么多年代码&#xff0c;依旧做不好一个项目做好一个项目是人力、产品、业务、技术、运营的结合&#xff0c;可能还叠加一点时机的因素&#xff0c;就我们码农而言&#xff0c;工作就是搬砖&#xff0c;实现产品&#xff0c; 给业务提供支撑。“给祖传代码加 BUG 修 BUG”…

IntelliJ IDEA中快捷键大全+出现的问题

参照 文章目录1.Ctrl2.Alt3.Shift4.Ctrl Alt5.Ctrl Shift6.Alt Shift7.Ctrl Shift Alt8.其他idea如何将设置包名展开当出现右键&#xff0c;没有创建包选项时idea没有maven图标安装mysql和idea配置idea查看代码的最近修改人及时间1.Ctrl 快捷键介绍Ctrl F在当前文件进行…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(八)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

[数据结构-严蔚敏版]P95矩阵压缩-特殊矩阵的存储(对称矩阵,三角矩阵)

对称矩阵的存储&#xff1a; 代码如下: #include <iostream> using namespace std;int main() {int n;cin >> n;int *a;a new int[(n*(n 1)) / 2];for (int i 0; i < (n*(n 1)) / 2; i){cin >> a[i];}for (int i 1; i < n; i){for (int j 1; j…

微前端与项目实施方案研究

一、前言微前端(micro-frontends)是近几年在前端领域出现的一个新概念&#xff0c;主要内容是将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块&#xff0c;而在用户看来仍然是内聚的单个产品。微前端的理念源于微服务&#xff0c;是将庞大的整体拆成可控的小…

ASP.NET Core分布式项目实战(集成ASP.NETCore Identity)--学习笔记

任务24&#xff1a;集成ASP.NETCore Identity之前在 Index 页面写了一个 strong 标签&#xff0c;需要加个判断再显示&#xff0c;不然为空没有错误的时候也会显示if (!ViewContext.ModelState.IsValid) {<strong>Error""</strong><div asp-validatio…

Java中关于省略作用域报错问题分析

这个是很典型的作用域问题&#xff0c;if后如果省略那么if只作用于其后面的第一行代码 这时候如果这行代码只是个变量声明语句的话&#xff0c;这个变量是没有其他任何逻辑可以访问到的&#xff0c;因为作用域问题(如果有/&#xff0c;那么声明语句中声明的变量只在这个个内可用…

C#9就这么来了,.NET开发者该做点什么?

就在上周三10号&#xff0c;.NET5.0发布了第5个预览版&#xff0c;同时支持了C#9-preview&#xff01;是的&#xff0c;你没看错&#xff0c;虽然C# 8.0还未正式发布&#xff0c;但是通往C&#xff03;9的漫长道路却已经开始&#xff0c;这发展速度简直了&#xff01;C#语言的快…

Magicodes.IE在.NET Core中通过请求头导出多种格式文件

原文作者&#xff1a;HueiFeng前言在2.2里程碑中我们增加了一些新的功能,正如标题所写通过请求头进行导出我们不同格式的文件.下面我们来看一下如何使用.通过这种方式无论是对我们的数据多用途&#xff0c;还是说对我们的数据校验都做到了轻松易配。同时我们也将在本周发布2.3版…