使用.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过滤条件时

  • 请求
    image

  • 响应
    image

请求仅携带PriorityLevel过滤条件时

  • 请求
    image

  • 响应
    image

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

  • 请求
    image

  • 响应
    image

请求参数不合法时

  • 请求
    image
    我将pageNumber传成了0。

  • 响应
    image

总结

对于查询过滤这个需求来说,实现起来还是很简单的,但是这里其实隐藏了一个很重要的问题:如果查询的参数太多,比如存在多个Guid类型的字段过滤,URL的总长度是有可能超出浏览器或者服务器Host环境限制的,在这种情况下,我们是否还要坚持使用符合理查森成熟度模型Level 2的GET请求呢?

关于这个问题的争论一直以来就没有停过,首先说我个人的结论:可以采用POST的方式去变通,没必要为难自己(虽然这话肯定会引来Restful拥趸的嘲讽)。可是如果对成熟度有硬性的要求,我们如何实现?比较通用的解决方案是将一步GET查询拆成两步GET查询。但是更多的情况,是我认为如果出现了这种情况,就放弃Restful风格吧。

其实GraphQL才是王道。

参考资料

  1. 理查森成熟度模型
  2. GraphQL
文章来源:https://www.cnblogs.com/code4nothing/p/build-todolist-14.html

版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
上一篇:C# Abp框架入门系列文章(一)
下一篇:记一次本地正常上线接口报404
评论列表

发表评论

评论内容
昵称:
验证码:
验证码
关联文章

使用.NET 6开发TodoList应用(14)——实现查询过滤
使用.NET 6开发TodoList应用(16)——实现查询排序
使用.NET 6开发TodoList应用(15)——实现查询搜索
使用.NET 6开发TodoList应用(13)——实现查询分页
使用.NET 6开发TodoList应用(12)——实现ActionFilter
使用.NET 6开发TodoList应用(22)——实现缓存
使用.NET 6开发TodoList应用(9)——实现PUT请求
使用.NET 6开发TodoList应用(25)——实现RefreshToken
使用.NET 6开发TodoList应用(17)——实现数据塑形
使用.NET 6开发TodoList应用(7)——使用AutoMapper实现GET请求
使用.NET 6开发TodoList应用(31)——实现基于Github Actions和ACI的CI/CD
使用.NET 6开发TodoList应用(6)——使用MediatR实现POST请求
使用.NET 6开发TodoList应用(29)——实现静态字符串本地化功能
使用.NET 6开发TodoList应用(24)——实现基于JWT的Identity功能
使用.NET 6开发TodoList应用(21)——实现API版本控制
使用.NET 6开发TodoList应用(28)——实现应用程序健康检查
使用.NET 6开发TodoList应用(30)——实现Docker打包和部署
使用.NET 6开发TodoList应用(11)——使用FluentValidation和MediatR实现接口请求验证
使用.NET 6开发TodoList应用(23)——实现请求限流
使用.NET 6开发TodoList应用(8)——实现全局异常处理

热门标签
.NET Core .NET Reactor ag-grid AI发布 api安全 ASP.NET Core C#DLL加密 C#播放声音 C#代码混淆 C#代码加密 ChromeDriver Codex DateTime DBeaver devexpress devTool DLL混淆 edge.js EF EFCore Electron element-ui el-form el-table excel FastReport FileStream FolderBrowerDialog FolderSelectDialog form提交 git gridcontrol gridview input javascript json字符串 JS转换对象JSON jwt JWT授权 linq log Math MCP mitmproxy MVC MySQL Navicat netstat nginx node_modules NSwag Nuget Nuget镜像 number PowerShell pyinstaller python pythoncom python爬虫 python抓包 pywin32 redis Requests-html RestSharp Selenium sql SQL Server Swagger to-cms Visual Studio VSCode vue VueRouter vue路由 VUE页面通讯 Webpack Windows Windows服务 winform wmi xlrd yaml YESCMS YESWEB开发框架 白象 表单提交 播放声音 打开URL 代码混淆 弹窗提醒 端口占用 对象转换 分布式 公共字典 机器码 进程排查 静态资源 开发指南 路由参数 密钥 配置教程 配置文件 权限 人工智能 任务 任务调度 日期间隔 日志 日志记录 省市区 授权验证 数据库 四舍五入 文案 文件读取 文件夹选择 文件目录选择 问题排查 行政区域数据 页面通讯 中间件 CSharp 事务锁 工单系统 并发控制 重复提交 CMS Markdig Markdown markdown-it marked 技术选型 VS Code 开发工具 源代码管理 版本控制 Docker PostgreSQL 时区 部署排查 CMS架构 EF Core 主题系统 二次开发 插件系统 容器 运维命令 镜像清理 Linux NAS 远程挂载 飞牛 fnOS
联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码