基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表

系列文章

  • 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客?

  • 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目

  • 基于.NetCore开发博客项目 StarBlog - (3) 模型设计

  • 基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入

  • 基于.NetCore开发博客项目 StarBlog - (5) 开始搭建Web项目

  • 基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表

  • ...

前言

前一篇文章把Web项目搭起来了,现在开始来写页面~

本文记录博客文章列表的开发,包括参数、分类过滤、分页、搜索、排序等内容。

ORM

本项目的ORM使用FreeSQL,前面「博客批量导入」的文章中有初步涉及到了,不过没有介绍太多,这里再讲一下几个关键的地方。

不同于网上比较常见的EF Core,FreeSQL设计完模型之后不需要进行迁移操作,在开发模式下开启自动结构同步(AutoSyncStructure)就能自动创建、修改数据表。

还有比较方便的一点是FreeSQL自带了简单的仓储模式,不用再自己封装一套,可以减少开发时的代码量~

不过局限性也是有的,不封装仓储层的话,意味着service层代码跟ORM绑定,以后如果切换ORM会带来额外的重构成本。

打开StarBlog.Data项目,我们来写一个扩展方法,新增Extensions目录,在里面新增ConfigureFreeSql.cs

using FreeSql;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;namespace StarBlog.Data.Extensions;public static class ConfigureFreeSql {public static void AddFreeSql(this IServiceCollection services, IConfiguration configuration) {var freeSql = new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, configuration.GetConnectionString("SQLite")).UseAutoSyncStructure(true).Build();services.AddSingleton(freeSql);// 仓储模式支持services.AddFreeRepository();}
}

然后编辑StarBlog.Web项目下的Program.cs,注册一下FreeSQL的服务,用我们刚才写的扩展方法。

using StarBlog.Data.Extensions;builder.Services.AddFreeSql(builder.Configuration);

在要用的地方注入就行了,比如

IBaseRepository<Post> _postRepo;// 获取全部文章
_postRepo.Select.ToList()

就很方便了,开箱即用~

Service

因为我们的后端既要渲染页面,又要做RESTFul接口,所以要把业务逻辑抽象出来放在service层,避免在Controller里重复。

StarBlog.Web项目的Services目录里新增PostService.cs,我们要在这封装跟文章有关的逻辑~

首先依赖注入,把需要用到的服务注入进来

public class PostService {private readonly IBaseRepository<Post> _postRepo;private readonly IBaseRepository<Category> _categoryRepo;public PostService(IBaseRepository<Post> postRepo,IBaseRepository<Category> categoryRepo) {_postRepo = postRepo;_categoryRepo = categoryRepo;}
}

写一个获取全部文章的方法

public List<Post> GetAll() {return _postRepo.Select.ToList();
}

这样就初步搞定了,接下来要来写Controller

Controller

StarBlog.Web项目的Controllers目录下,新增BlogController.cs,用来实现跟博客有关的接口。

注入刚刚写好的 PostService

public class BlogController : Controller {private readonly PostService _postService;public BlogController(PostService postService) {_postService = postService;}
}

写文章列表“接口”(MVC也算接口吧)

public IActionResult List() {return View(_postService.GetAll());
}

View

根据AspNetCore MVC项目的约定,要把网页模板放在Views目录下,按Controller分类

这个文章列表页面,按照约定的路径是:Views/Blog/List.cshtml,创建这个文件

@model List<Post>
@{ViewData["Title"] = "博客列表";
}
<div class="container px-4 py-3">@foreach (var post in Model) {<div class="card mb-3"><div class="card-header">@Model.Category.Name</div><div class="card-body"><h5 class="card-title">@Model.Title</h5><p class="card-text">@Model.Summary</p><a class="btn btn-outline-secondary stretched-link"asp-controller="Blog" asp-action="Post" asp-route-id="@Model.Id">查看全文</a></div></div>}
</div>

这样简单的文章列表就完成了

试试效果

运行项目,打开浏览器,输入地址http://127.0.0.1:5038/Blog/List,可以看到文章列表如下,很简单(简陋),而且全部文章都显示出来了,页面很长,这很明显并不是我们想要的最终效果。

58ef2fe8c6c6212992b523a819c5324c.png
image

不急,接下来慢慢来优化。

分页

首先是页面把全部文章都显示出来的问题,我们需要引入分页功能

分页可以自己实现,也可以用第三方组件,我们用的FreeSQL也支持分页的API,这里我直接掏出之前做项目用过的X.PagedList,它封装了分页取数据和前端的分页部件,比较方便。

直接nuget里安装这两个包就行:

  • X.PagedList

  • X.PagedList.Mvc.Core

使用很简单,X.PagedList组件定义了List类型的扩展方法,直接在ORM读取出来的List上用就行

_postRepo.Select.ToList().ToPagedList(pageNumber, pageSize);

返回类型是IPagedList<T>,除了当前页面的数据,还包含有分页的信息(当前页面、总页面数量、页面大小、总数据量等),可以直接当List用。

然后X.PagedList组件还封装了MVC模板上的HTML组件,使用也很简单:

<nav aria-label="Page navigation example">@Html.PagedListPager(Model.Posts, page => Url.Action(RazorHelper.GetCurrentActionName(ViewContext), new {page, categoryId = Model.CurrentCategoryId}),new PagedListRenderOptions {LiElementClasses = new[] {"page-item"},PageClasses = new[] {"page-link"},UlElementClasses = new[] {"pagination justify-content-center"}})
</nav>

前端我要使用bootstrap的分页组件,所以把bootstrap的class传进去,如果是其他前端组件库的话,只需要传对应的class名称就行。

渲染出来的页面代码是这样的:

<div class="pagination-container"><ul class="pagination justify-content-center"><li class="active page-item"><span class="page-link">1</span></li><li class="page-item"><a class="page-link" href="/Blog/List?page=2&amp;categoryId=0">2</a></li><li class="page-item"><a class="page-link" href="/Blog/List?page=3&amp;categoryId=0">3</a></li><li class="page-item"><a class="page-link" href="/Blog/List?page=4&amp;categoryId=0">4</a></li><li class="page-item"><a class="page-link" href="/Blog/List?page=5&amp;categoryId=0">5</a></li><li class="page-item"><a class="page-link" href="/Blog/List?page=6&amp;categoryId=0">6</a></li><li class="page-item"><a class="page-link" href="/Blog/List?page=7&amp;categoryId=0">7</a></li><li class="page-item"><a class="page-link" href="/Blog/List?page=8&amp;categoryId=0">8</a></li><li class="page-item"><a class="page-link" href="/Blog/List?page=9&amp;categoryId=0">9</a></li><li class="page-item"><a class="page-link" href="/Blog/List?page=10&amp;categoryId=0">10</a></li><li class="PagedList-ellipses page-item"><a class="PagedList-skipToNext page-link" href="/Blog/List?page=11&amp;categoryId=0" rel="next">…</a></li><li class="PagedList-skipToNext page-item"><a class="page-link" href="/Blog/List?page=2&amp;categoryId=0" rel="next">&gt;</a></li><li class="PagedList-skipToLast page-item"><a class="page-link" href="/Blog/List?page=64&amp;categoryId=0">&gt;&gt;</a></li></ul>
</div>

显示效果:

51babda73c16f3a2cb03e31f7fa21c7b.png
image

请求参数封装

前面介绍的分页需要在访问页面时传入请求参数,这样我们Controller的Action方法就需要加上pageNumberpageSize这两个参数,后面还要加文章分类筛选和搜索排序什么的,这样参数太多了,全都写在Action方法的参数里不优雅,好在AspNetCore提供了class作为参数的写法。

StarBlog.Web/ViewModels目录下新建QueryFilters目录,用来存不同接口的请求参数。

有些参数属于不同接口都有的,合理利用面向对象,先写个基类:QueryParameters.cs

public class QueryParameters {/// <summary>/// 最大页面条目/// </summary>public const int MaxPageSize = 50;private int _pageSize = 10;/// <summary>/// 页面大小/// </summary>public int PageSize {get => _pageSize;set => _pageSize = (value > MaxPageSize) ? MaxPageSize : value;}/// <summary>/// 当前页码/// </summary>public int Page { get; set; } = 1;/// <summary>/// 搜索关键词/// </summary>public string? Search { get; set; }/// <summary>/// 排序字段/// </summary>public string? SortBy { get; set; }
}

文章请求参数在此基础上还增加了状态、分类等,从上面这个基类派生一个新类就好:PostQueryParameters.cs

public class PostQueryParameters : QueryParameters {/// <summary>/// 仅请求已发布文章/// </summary>public bool OnlyPublished { get; set; } = false;/// <summary>/// 文章状态/// </summary>public string? Status { get; set; }/// <summary>/// 分类ID/// </summary>public int CategoryId { get; set; } = 0;/// <summary>/// 排序字段/// </summary>public new string? SortBy { get; set; } = "-LastUpdateTime";
}

service改造

我们的核心逻辑都是在service中实现的,请求参数肯定也要传入给service来使用。

依然是先前的GetPagedList方法,给其加上各种筛选条件之后是这样:

public IPagedList<Post> GetPagedList(PostQueryParameters param) {var querySet = _postRepo.Select;// 是否发布if (param.OnlyPublished) {querySet = _postRepo.Select.Where(a => a.IsPublish);}// 状态过滤if (!string.IsNullOrEmpty(param.Status)) {querySet = querySet.Where(a => a.Status == param.Status);}// 分类过滤if (param.CategoryId != 0) {querySet = querySet.Where(a => a.CategoryId == param.CategoryId);}// 关键词过滤if (!string.IsNullOrEmpty(param.Search)) {querySet = querySet.Where(a => a.Title.Contains(param.Search));}// 排序if (!string.IsNullOrEmpty(param.SortBy)) {// 是否升序var isAscending = !param.SortBy.StartsWith("-");var orderByProperty = param.SortBy.Trim('-');querySet = querySet.OrderByPropertyName(orderByProperty, isAscending);}return querySet.Include(a => a.Category).ToList().ToPagedList(param.Page, param.PageSize);
}

根据传入的参数,可以实现状态过滤、分类过滤、关键词过滤、排序和分页功能。

ViewModel

一个MVC页面只能指定一个Model,虽然可以用弱类型的ViewBag或者ViewData,但是弱类型不好维护,我们来定义一个ViewModel给页面使用。

先确定要在文章列表页面显示哪些内容,例如显示当前选择的文章分类、所有分类列表。

StarBlog.WebViewModels目录下,新建BlogListViewModel.cs,根据我们要展示的内容,定义模型如下

using StarBlog.Data.Models;
using X.PagedList;namespace StarBlog.Web.ViewModels; public class BlogListViewModel {public Category CurrentCategory { get; set; }public int CurrentCategoryId { get; set; }public IPagedList<Post> Posts { get; set; }public List<Category> Categories { get; set; }
}

搞定。

controller改造

经过前面的铺垫,controller这里就简单了,不过还有要注意的地方,本项目是包含后端渲染和RESTFul接口两部分的,因此controller要写两个,service只要一个就行。

RESTFul接口我后面再具体介绍,可以先看看改造后的RESTFul接口controller的代码:

[AllowAnonymous]
[HttpGet]
public ApiResponsePaged<Post> GetList([FromQuery] PostQueryParameters param) {var pagedList = _postService.GetPagedList(param);return new ApiResponsePaged<Post> {Message = "Get posts list",Data = pagedList.ToList(),Pagination = pagedList.ToPaginationMetadata()};
}

代码很简单,这个获取文章列表的接口,就单纯只需要给分页和过滤后的列表数据就行。

而MVC的接口就没这么简单,要显示在页面上的东西,全都要在后端做渲染,包括我们在前面说的要显示当前分类、所有分类列表。

代码长这样:

public IActionResult List(int categoryId = 0, int page = 1, int pageSize = 5) {var categories = _categoryRepo.Where(a => a.Visible).IncludeMany(a => a.Posts).ToList();categories.Insert(0, new Category { Id = 0, Name = "All", Posts = _postRepo.Select.ToList() });return View(new BlogListViewModel {CurrentCategory = categoryId == 0 ? categories[0] : categories.First(a => a.Id == categoryId),CurrentCategoryId = categoryId,Categories = categories,Posts = _postService.GetPagedList(new PostQueryParameters {CategoryId = categoryId,Page = page,PageSize = pageSize,OnlyPublished = true})});
}

传入参数只需要三个:

  • 分类ID

  • 当前页面

  • 页面大小

这个接口要做的事比较多

  • 获取所有分类

  • 判断当前分类

  • 获取文章列表

最终返回我们前面定义的BlogListViewModel

然后在页面模板里就可以用了。

View改造

第一件事把model换成BlogListViewModel

然后就是根据ViewModel里的数据进行页面渲染,都是Bootstrap提供的页面组件,代码比较长我就不贴了,页面模板的完整代码可以在这看到:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Web/Views/Blog/List.cshtml

最终效果

截了个长图,最终的页面效果就是这样了~

b5c004cc0ba7f4b5811682489e83269e.png
image

小结

如果你看到了这里,说明你是个有耐心的人 O(∩_∩)O哈哈~,同时对本项目是比较感兴趣的,先感谢大家的支持~

本文一不小心就写得比较长了,本来是想以那种每篇文章比较短的形式做一个连载,这样读起来不会有太大的压力,没想到稍微一展开讲就涉及到很多内容,接下来的文章我得优化优化~

最近一段时间,公众号后台、微信都有收到朋友的催更,或者是抱怨我更新得太慢,实在是抱歉,最近被工作上的事情搞得有点晕头转向的,下班回家后晚上就只想看会书或者玩一下游戏放松,懈怠了,看到有这么多大佬在关注我的项目,顿时又充满动力了!冲冲冲,接下来争取每两天更新一篇,欢迎继续关注1ef1de0b2ce7d9449dfb20a1a9c10aa3.png~

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

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

相关文章

《看聊天记录都学不会C语言?太菜了吧》(12)循环有多容易?你看一眼就怀...

若是大一学子或者是真心想学习刚入门的小伙伴可以私聊我&#xff0c;若你是真心学习可以送你书籍&#xff0c;指导你学习&#xff0c;给予你目标方向的学习路线&#xff0c;无套路&#xff0c;博客为证。 本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖…

***ECharts图表入门和最佳实践

ECharts数据图表系统&#xff1f; 5分钟上手&#xff01; 【ECharts简介】 ECharts开源来自百度商业前端数据可视化团队&#xff0c;基于html5 Canvas&#xff0c;是一个纯Javascript图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数…

WPF|一个比较简单带点设计的登录界面

阅读目录效果展示准备简单说明 源码结尾&#xff08;视频及源码仓库&#xff09;1. 效果展示欣赏效果&#xff1a;2. 准备创建一个WPF工程&#xff0c;比如站长使用 .NET 7[1] 创建名为 Login5 的WPF项目。找一张图片做为装饰&#xff0c;放登录表单左侧&#xff1a;添加Nuget…

Android插件基础之类加载器学习

记录学习java 加载器学习所获心得&#xff0c;逐步记录了解java加载器的过程。为了知悉android 插件化的实现原理&#xff0c;从而需要从头了解android加载apk&#xff0c;以及基础的java类加载的加载过程情况&#xff0c;为方便记录和记忆&#xff0c;故此将学习了解的过程记录…

《看聊天记录都学不会C语言?太菜了吧》(13)(9*9 乘法表)寻找电脑中的盲盒彩蛋

若是大一学子或者是真心想学习刚入门的小伙伴可以私聊我&#xff0c;若你是真心学习可以送你书籍&#xff0c;指导你学习&#xff0c;给予你目标方向的学习路线&#xff0c;无套路&#xff0c;博客为证。 本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖…

Fragment使用--文章集锦

android使用Fragment实现底部菜单使用show()和hide()来切换以保持Fragment状态Android Fragment 真正的完全解析&#xff08;上&#xff09;Android Fragment实践(一)纠正对Fragment Transaction BackStack的误解多个Fragment 切换时不重新实例化Fragment详解之四——管理Fragm…

使用 Postman 实现 API 自动化测试

1背景介绍 相信大部分开发人员和测试人员对 postman 都十分熟悉&#xff0c;对于开发人员和测试人员而言&#xff0c;使用 postman 来编写和保存测试用例会是一种比较方便和熟悉的方式。但 postman 本身是一个图形化软件&#xff0c;相对较难或较麻烦&#xff08;如使用 …

php json -gt;访问,【转】Php+ajax+jsonp解决ajax跨域问题

首先&#xff1a;jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。发送端&#xff1a;$.ajax({type : "post",url : "ajax.php",dataType : "jsonp",jsonp: "callback",//传递给请求处理程序或页面的&…

《看聊天记录都学不会Python到游戏实战?太菜了吧》(1)加载Python神器!亮剑!

本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新&#xff0c;包括别的语言以及实战都将使用对话的方式进行教学&#xff0c;基础编程语言教学适用于零基础小白&#xff0c;之后实战课程也将会逐步更新。 若…

Codeigniter 3 拓展HMVC

2019独角兽企业重金招聘Python工程师标准>>> 在Codeiniter&#xff08;以下统称CI&#xff09; 2.X版本中&#xff0c;我们就通过拓展核心类库实现了HMVC&#xff0c;但是同样的代码&#xff0c;拿到CI 3中&#xff0c;就很有可能不好用了。 ###拓展核心类库方式 官…

玩转ASP.NET Core 6.0框架-序言

ASP.NET Core是微软提供的强大的web框架&#xff0c;它有很多潜在的强大而有用的功能。本专栏的目标是帮助您把框架的隐藏能力最大限度地发挥出来&#xff0c;让您能够按需定制ASP NET Core框架。本专栏提供了一种具体的操作和实现方法&#xff0c;可以让你在短时间尽可能地提高…

从C#开始的面向对象编程经典自学教程

1、C语言的时代 在C语言时代,可以认为那时的编程过程就是表格加工过程,也就是从A表格到B表格、再到C表格…..等等,这个过程中,计算机语言如何描述一个表格,成为计算机语言的关键。 对一个表格的分析可以发现:表格是两部分组成:表头和表格: 学生表 STUDENT 在这个表格…

《看聊天记录都学不会Python到游戏实战?太菜了吧》(2)在python中加号竟然如此善变

本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新&#xff0c;包括别的语言以及实战都将使用对话的方式进行教学&#xff0c;基础编程语言教学适用于零基础小白&#xff0c;之后实战课程也将会逐步更新。 若…

mysql数据库rp集群,使用MySQL-Cluster搭建MySQL数据库集群

1、MySQL集群的作用&#xff1a;- 解决访问节点的单点故障- 数据存储节点的单点故障- 解决数据存储节点数据备份问题2、集群&#xff1a;使用一组服务器提供相同的服务3、关于MySQL-Cluster&#xff1a;MySQL官方提供的集群版本已集成标准版MySQL程序&#xff0c;可独立安装使用…

桌面虚拟化云技术将支撑数字化医院

桌面虚拟化云技术将支撑数字化医院 2013-12-04 10:32 现今医疗行业已经从医院——患者的二元关系走向从医院——患者——政府监督——医疗保险的多元关系&#xff0c;医疗体系需要更高效的运转&#xff0c;患者需要更好的就医体验&#xff0c;这使得医疗行业信息化建设的重要性…

《看聊天记录都学不会C语言?太菜了吧》(14)这么神奇?我写了20行代码竟然一行就可以搞定?

若是大一学子或者是真心想学习刚入门的小伙伴可以私聊我&#xff0c;若你是真心学习可以送你书籍&#xff0c;指导你学习&#xff0c;给予你目标方向的学习路线&#xff0c;无套路&#xff0c;博客为证。 本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖…

【经典回放】多种语言系列数据结构算法:栈(C#版)

本文采用C#语言实现栈结构算法并举例应用。 一、C#的栈对象使用 对C#而言,要使用栈是极其简单的事情,因为C#已经有栈对象,所以根本不需要自用再用顺序表构造栈,只需要你会说明、会应用即可。 在C#中栈对象的类型名称是Stack,这是个泛型的对象,所以,你在使用这个对象的时…

在 Kubernetes 上部署 Secret 加密系统 Vault

HashiCorp Vault 是一个基于身份的 Secret 和加密管理系统。Secret 是您想要严格控制访问的内容&#xff0c;例如 API 加密密钥、密码或证书。Vault 提供由身份验证和授权方法控制的加密服务。使用 Vault 的 UI、CLI 或 HTTP API&#xff0c;可以安全地存储和管理对机密和其他敏…

《看聊天记录都学不会C语言?太菜了吧》(15)你学了一节课的函数我5分钟搞定了,还很熟

若是大一学子或者是真心想学习刚入门的小伙伴可以私聊我&#xff0c;若你是真心学习可以送你书籍&#xff0c;指导你学习&#xff0c;给予你目标方向的学习路线&#xff0c;无套路&#xff0c;博客为证。 本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖…

【经典回放】多种语言系列数据结构算法:栈(JavaScript版)

本文采用JavaScript语言实现栈结构算法并举例应用。 JavaScript的栈是数组对象中自动提供的,这点如同C#,实际也不需要自己编写什么栈的程序,如果你用: var s=new Array(); 定义一个s的数组,则这个s上将有以下的方法可供使用: 其中:FF: Firefox, IE: Internet Explore…