使用.NET 6开发TodoList应用(14)——实现查询过滤
系列导航及源代码
需求
在查询请求中,还有一类常见的场景是过滤查询,也就是有限制条件的查询,落在数据库层面就是常用的Where查询子句。实现起来也很简单。
目标
实现查询过滤的功能
原理与思路
查询过滤的请求有两种方式,一种是采用POST方法,将查询条件放在请求体中,但是这种方式实际上和Restful的动词语义产生了矛盾,从Restful API成熟度模型的角度来说,还停留在Level 1阶段(仅知道地址,不符合动词语义),详情参考理查森成熟度模型;还有一种方法是采用GET方法,将查询条件放在查询字符串里,我们将采用第二种方式来实现。
实现
我们还是通过查询TodoItem列表来演示查询过滤,和前面的文章一样,我们先来实现一个查询的Query请求对象:
GetTodoItemsWithConditionQuery.cs
using AutoMapper;
using AutoMapper.QueryableExtensions;
using MediatR;
using TodoList.Application.Common.Interfaces;
using TodoList.Application.Common.Mappings;
using TodoList.Application.Common.Models;
using TodoList.Domain.Entities;
using TodoList.Domain.Enums;
namespace TodoList.Application.TodoItems.Queries.GetTodoItems;
public class GetTodoItemsWithConditionQuery : IRequest<PaginatedList<TodoItemDto>>
{
public Guid ListId { get; set; }
public bool? Done { get; set; }
public PriorityLevel? PriorityLevel { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 10;
}
public class GetTodoItemsWithConditionQueryHandler : IRequestHandler<GetTodoItemsWithConditionQuery, PaginatedList<TodoItemDto>>
{
private readonly IRepository<TodoItem> _repository;
private readonly IMapper _mapper;
public GetTodoItemsWithConditionQueryHandler(IRepository<TodoItem> repository, IMapper mapper)
{
_repository = repository;
_mapper = mapper;
}
public async Task<PaginatedList<TodoItemDto>> Handle(GetTodoItemsWithConditionQuery request, CancellationToken cancellationToken)
{
return await _repository
.GetAsQueryable(x => x.ListId == request.ListId
&& (!request.Done.HasValue || x.Done == request.Done)
&& (!request.PriorityLevel.HasValue || x.Priority == request.PriorityLevel))
.OrderBy(x => x.Title)
.ProjectTo<TodoItemDto>(_mapper.ConfigurationProvider)
.PaginatedListAsync(request.PageNumber, request.PageSize);
}
}
甚至为了结合之前讲的请求校验,我们可以在这里增加一个校验规则:
GetTodoItemValidator.cs
using FluentValidation;
namespace TodoList.Application.TodoItems.Queries.GetTodoItems;
public class GetTodoItemValidator : AbstractValidator<GetTodoItemsWithConditionQuery>
{
public GetTodoItemValidator()
{
RuleFor(x => x.ListId).NotEmpty().WithMessage("ListId is required.");
RuleFor(x => x.PageNumber).GreaterThanOrEqualTo(1).WithMessage("PageNumber at least greater than or equal to 1.");
RuleFor(x => x.PageSize).GreaterThanOrEqualTo(1).WithMessage("PageSize at least greater than or equal to 1.");
}
}
接下来在TodoItemController中实现请求处理,我们直接修改上一篇讲分页的那个请求成如下(如果在上一篇的基础上新增Action的话,会导致路由的歧义):
TodoItemController.cs
// 省略其他...
[HttpGet]
public async Task<ApiResponse<PaginatedList<TodoItemDto>>> GetTodoItemsWithCondition([FromQuery] GetTodoItemsWithConditionQuery query)
{
return ApiResponse<PaginatedList<TodoItemDto>>.Success(await _mediator.Send(query));
}
验证
启动Api项目,执行创建TodoList的请求:
请求仅携带Done过滤条件时
请求

响应

请求仅携带PriorityLevel过滤条件时
请求

响应

请求携带完整的过滤条件时
请求

响应

请求参数不合法时
请求

我将pageNumber传成了0。响应

总结
对于查询过滤这个需求来说,实现起来还是很简单的,但是这里其实隐藏了一个很重要的问题:如果查询的参数太多,比如存在多个Guid类型的字段过滤,URL的总长度是有可能超出浏览器或者服务器Host环境限制的,在这种情况下,我们是否还要坚持使用符合理查森成熟度模型Level 2的GET请求呢?
关于这个问题的争论一直以来就没有停过,首先说我个人的结论:可以采用POST的方式去变通,没必要为难自己(虽然这话肯定会引来Restful拥趸的嘲讽)。可是如果对成熟度有硬性的要求,我们如何实现?比较通用的解决方案是将一步GET查询拆成两步GET查询。但是更多的情况,是我认为如果出现了这种情况,就放弃Restful风格吧。
其实GraphQL才是王道。
参考资料
使用.NET 6开发TodoList应用(14)——实现查询过滤的更多相关文章
- 使用.NET 6开发TodoList应用(13)——实现查询分页
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 查询中有个非常常见的需求就是后端分页,实现的方式也不算复杂,所以我们本文仅仅演示一个后端查询分页的例子. 目标 实现分页查询返 ...
- 使用.NET 6开发TodoList应用(15)——实现查询搜索
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 本文我们继续来看查询过程中的另外一个需求:搜索.搜索的含义是目标字段的全部或者部分值匹配请求中的搜索条件,对应到数据库层面是C ...
- 使用.NET 6开发TodoList应用(16)——实现查询排序
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 关于查询的另一个需求是要根据前端请求的排序字段进行对结果相应的排序. 目标 实现根据排序要求返回排序后的结果 原理与思路 要实 ...
- 使用.NET 6开发TodoList应用(3)——引入第三方日志库
需求 在我们项目开发的过程中,使用.NET 6自带的日志系统有时是不能满足实际需求的,比如有的时候我们需要将日志输出到第三方平台上,最典型的应用就是在各种云平台上,为了集中管理日志和查询日志,通常会选 ...
- 使用.NET 6开发TodoList应用(1)——系列背景
前言 想到要写这样一个系列博客,初衷有两个:一是希望通过一个实践项目,将.NET 6 WebAPI开发的基础知识串联起来,帮助那些想要入门.NET 6服务端开发的朋友们快速上手,对使用.NET 6开发 ...
- 使用.NET 6开发TodoList应用(2)——项目结构搭建
为了不影响阅读的体验,我把系列导航放到文章最后了,有需要的小伙伴可以直接通过导航跳转到对应的文章 : P TodoList需求简介 首先明确一下我们即将开发的这个TodoList应用都需要完成什么功能 ...
- 使用.NET 6开发TodoList应用(4)——引入数据存储
需求 作为后端CRUD程序员(bushi,数据存储是开发后端服务一个非常重要的组件.对我们的TodoList项目来说,自然也需要配置数据存储.目前的需求很简单: 需要能持久化TodoList对象并对其 ...
- 使用.NET 6开发TodoList应用(5)——领域实体创建
需求 上一篇文章中我们完成了数据存储服务的接入,从这一篇开始将正式进入业务逻辑部分的开发. 首先要定义和解决的问题是,根据TodoList项目的需求,我们应该设计怎样的数据实体,如何去进行操作? 长文 ...
- 使用.NET 6开发TodoList应用(5.1)——实现Repository模式
需求 经常写CRUD程序的小伙伴们可能都经历过定义很多Repository接口,分别做对应的实现,依赖注入并使用的场景.有的时候会发现,很多分散的XXXXRepository的逻辑都是基本一致的,于是 ...
随机推荐
- TLSv1.3 Support:主流 Web 客户端和服务端对 TLSv1.3 的支持情况
TLSv1.3 Support:主流 Web 客户端和服务端对 TLSv1.3 的支持情况 请访问原文链接:https://sysin.org/blog/tlsv1-3-support/,查看最新版. ...
- API接口设计之token、timestamp、sign 具体架构与实现(APP/小程序,传输安全)
Java生鲜电商平台-API接口设计之token.timestamp.sign 具体设计与实现 说明:在实际的业务中,难免会跟第三方系统进行数据的交互与传递,那么如何保证数据在传输过程中的安全呢(防窃 ...
- gitlab之数据备份恢复
备份#备份的时候,先通知相关人员服务要听 ,停止两个服务,并影响访问 root@ubuntu:/opt/web1# gitlab-ctl stop unicorn ok: down: unicorn: ...
- Java Spring 自定义事件监听
ApplicationContext 事件 定义一个context的起动监听事件 import org.springframework.context.ApplicationListener; imp ...
- 【Service】【Database】【MySQL】基础概念
1. 数据模型:层次模型.网状模型.关系模型 关系模型: 二维关系: 表:row, column 索引:index 视图:view 2. SQL接口:Structured Query Language ...
- 【Java基础】方法调用机制——MethodHandle
MethodHandle是Java7引入的一种机制,主要是为了JVM支持动态语言. 一个MethodHandle调用示例 共有方法调用 首先,演示一下最基本的MethodHandle使用. 第一步:创 ...
- 十二. Go并发编程--sync/errGroup
一.序 这一篇算是并发编程的一个补充,起因是当前有个项目,大概の 需求是,根据kafka的分区(partition)数,创建同等数量的 消费者( goroutine)从不同的分区中消费者消费数据,但是 ...
- win10更新后任务栏卡死 的原因和解决办法
@ 目录 现象: 原因: 第一步:断网并关闭资讯和兴趣 第二步:卸载更新 第三步:关闭win10自动更新 第四步:永久关闭资讯和兴趣 现象: win10 更新后,开机任务栏卡死,点开始反应,设置页面无 ...
- 一种基于Java Swing/HTML/MySQL的汽车租赁系统
该项目是一个Java的课程作业(大二),主要运用Java.Swing.HTML.MySQL,实现基本的租车逻辑.界面可视化.信息导出.数据存储等功能.实现管理员.用户两种角色登录,并结合Java开发中 ...
- 30个类手写Spring核心原理之自定义ORM(上)(6)
本文节选自<Spring 5核心原理> 1 实现思路概述 1.1 从ResultSet说起 说到ResultSet,有Java开发经验的"小伙伴"自然最熟悉不过了,不过 ...