ASP.NET Core WebAPI学习-4

2023-11-17

  1. ASP.NET Core WebAPI学习-1
  2. ASP.NET Core WebAPI学习-2
  3. ASP.NET Core WebAPI学习-3
  4. ASP.NET Core WebAPI学习-4
  5. ASP.NET Core WebAPI学习-5
  6. ASP.NET Core WebAPI学习-6

查询和搜索

数据绑定

数据可以通过多种方式来传给API。
Binding Source Attributes 会告诉 Model 的绑定引擎从哪里找到绑定源。
共有以下六种 Binding Source Attributes:
[FromBody] 请求的 Body
[FromForm] 请求的 Body 中的 form数据
[FromHeader] 请求的 Header
[FromQuery] Query string 参数
[FromRoute] 当前请求中的路由数据
[FromService] 作为 Action 参数而注入的服务
默认情况下ASP.NET Core 会使用 Complex Object Model Binder,它会把数据从Value Providers那里提取出来,而Value Providers的顺序是定义好的。
但是我们构建API时通常会使用 [ApiController] 这个属性,为了更好的适应API它改变了上面的规则。更改后的规则如下:
[FromBody] 通常是用来推断复杂类型参数的。
[FromForm] 通常用来推断IFormFile和IFormFileCollection类型的Action参数。
[FromRoute] 用来推断Action的参数名和路由模板中的参数名一致的情况。
[FromQuery] 用来推断其它的Action参数。
按照这些规则,在Action的参数前面使用这些属性,就可以避免让我们手动去寻找绑定源。当默认的行为规则需要被重写的时候,也可以使用这些 Binding Source Attributes.

过滤

过滤集合的意思就是指根据条件限定返回的集合。
例如我想返回所有类型为国有企业的欧洲公司。则URI为:GET /api/companies?type=State-owned&region=Europe
所以过滤就是指:我们把某个字段的名字以及想要让该字段匹配的值一起传递给API,并将这些作为返回的集合的一部分。

搜索

针对集合进行搜索是指根据预定义的一些规则,把符合条件的数据添加到集合里面。
搜索实际上超出了过滤的范围。针对搜索,通常不会把要匹配的字段名传递过去,通常会把要搜索的值传递给API,然后API自行决定应该对哪些字段来查找该值。经常会是全文搜索。
例如:GET /api/companies?q=xxx

过滤 vs 搜索

可以看出来过滤和搜索是不同的。
过滤:首先是一个完整的集合,然后根据条件把匹配/不匹配的数据项移除。
搜索:首先是一个空的集合,然后根据条件把匹配/不匹配的数据项往里面添加。

但需要注意的是:

过滤和搜索这些参数并不是资源的一部分。
只允许针对资源的字段进行过滤。
过滤:

[HttpGet]
public async Task<ActionResult<IEnumerable<EmployeeDto>>>
    GetEmployeesForCompany(Guid companyId, [FromQuery(Name = "gender")]string genderDisplay)
{
    if (!await companyRepository.CompanyExistsAsync(companyId))
    {
        return NotFound();
    }
    var employees = await companyRepository.GetEmployeesAsync(companyId, genderDisplay);
    var employeeDtos = mapper.Map<IEnumerable<EmployeeDto>>(employees);
    return Ok(employeeDtos);
}

public async Task<IEnumerable<Employee>> GetEmployeesAsync(Guid companyId, string gender)
{
    if (companyId == Guid.Empty)
    {
        throw new ArgumentNullException(nameof(companyId));
    }
    if (string.IsNullOrWhiteSpace(gender))
    {
        return await context.Employees
            .Where(x => x.CompanyId == companyId)
            .OrderBy(x => x.EmployeeNo)
            .ToListAsync();
    }

    //判断Gender是否转换成功
    Gender gender1;
    var isGender = Enum.TryParse<Gender>(gender.Trim(), out gender1);
    if (isGender)
    {
        return await context.Employees
            .Where(x => x.CompanyId == companyId && x.Gender == gender1)
            .OrderBy(x => x.EmployeeNo)
            .ToListAsync();
    }
    else
    {
        throw new ArgumentNullException(nameof(gender));
    }

}

查询

注意如果使用复杂类型查询,这里的CompanyDtoParameter时,API默认绑定的类型为从Body中获取,会出现415(Unsupported Media Type)错误,这时要在参数前面加上FromQuery特性,让参数从查询字符串中获取

  1. 因为ApiController的复杂类型参数默认从FromBody中推断,所以加入[FromQuery]即可
  2. 如果不指定Content-Type也会出现该错误,在Post的时候,在Header头部添加Content-Type:Application/json
[HttpGet(Name = nameof(GetCompanies))]
[HttpHead]
public async Task<ActionResult<IEnumerable<CompanyDto>>> GetCompanies([FromQuery]CompanyDtoParameter parameter = null)
{
    var companies = await companyRepository.GetCompaniesAsync(parameter);
    var previousPageLink = companies.HasPrevious
        ? CreateCompaniesResourceUri(parameter, ResourceUriType.PreviousePage)
        : null;
    var nextPageLink = companies.HasNext
        ? CreateCompaniesResourceUri(parameter, ResourceUriType.NextPage)
        : null;
    var paginationMetadata = new
    {
        totalCount = companies.TotalCount,
        pageSize = companies.PageSize,
        currentPage = companies.CurrentPage,
        totalPages = companies.TotalPages,
        previousPageLink,
        nextPageLink
    };
    Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(paginationMetadata, new JsonSerializerOptions()
                                                                  {
                                                                      Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
                                                                  }));

    var companyDtos = mapper.Map<IEnumerable<CompanyDto>>(companies);

    return Ok(companyDtos);
}

创建资源

注意几个方法的使用:
UnprocessableEntity: 返回422错误
BadRequest: 返回400错误
CreatedAtRoute:返回创建成功的201状态码

[HttpPost]
        public async Task<ActionResult<CompanyDto>> CreateCompany([FromBody]CompanyAddDto company)
        {
            if (!ModelState.IsValid)
            {
                //Creates an Microsoft.AspNetCore.Mvc.UnprocessableEntityObjectResult that produces
                //a Microsoft.AspNetCore.Http.StatusCodes.Status422UnprocessableEntity response.
                return UnprocessableEntity(ModelState);
            }
            if (company == null)
            {
                //Creates an Microsoft.AspNetCore.Mvc.BadRequestResult that produces 
                //a Microsoft.AspNetCore.Http.StatusCodes.Status400BadRequest response.
                return BadRequest();
            }
            var entity = mapper.Map<Company>(company);
            companyRepository.AddCompany(entity);
            await companyRepository.SaveAsync();
            var returndto = mapper.Map<CompanyDto>(entity);
            //Creates a Microsoft.AspNetCore.Mvc.CreatedAtRouteResult object that produces
            //a Microsoft.AspNetCore.Http.StatusCodes.Status201Created response.
            return CreatedAtRoute(nameof(GetCompany), routeValues: new { companyId = returndto.Id }, value: returndto);
        }

发送请求:
在这里插入图片描述
请求创建实体成功的响应:
请求创建实体成功的响应
请求创建实体时传递的参数错误时的响应:
在这里插入图片描述
请求创建实体时传递的参数错误时的响应:
在这里插入图片描述

自定义模型绑定器

定义:

using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace Routine.Api.Helpers
{
    /// <summary>
    /// 自定义Model绑定器
    /// </summary>
    public class ArrayModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            //判断传递的参数的类型是否为枚举类型
            if (!bindingContext.ModelMetadata.IsEnumerableType)
            {
                bindingContext.Result = ModelBindingResult.Failed();
                return Task.CompletedTask;
            }

            //判断传递的值是否为空,如果为空的话直接返回,在Controller中返回BadRequest
            var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ToString();
            if (string.IsNullOrWhiteSpace(value))
            {
                bindingContext.Result = ModelBindingResult.Success(null);
                return Task.CompletedTask;
            }

            /*利用反射把string转换为guid数组*/
            //获取guid类型
            var elementType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];
            //获取转换器是为了把字符串类型转换为guid类型
            var converter = TypeDescriptor.GetConverter(elementType);
            //把string转换为object数组
            var values = value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                .Select(x => converter.ConvertFromString(x.Trim())).ToArray();
            //把object数组转换为guid数组
            var typedValues = Array.CreateInstance(elementType, values.Length);
            values.CopyTo(typedValues,0);

            bindingContext.Model = typedValues;
            bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
            return Task.CompletedTask;
        }
    }
}

使用:

/// <summary>
/// 使用自定义模型绑定器,查询公司集合
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[HttpGet("({ids})", Name = nameof(GetCompanyCollection))]
public async Task<IActionResult> GetCompanyCollection(
    [FromRoute]
    [ModelBinder(BinderType =typeof(ArrayModelBinder))]
    IEnumerable<Guid> ids)
{
    if (ids == null)
    {
        return BadRequest();
    }
    var entities = await companyRepository.GetCompaniesAsync(ids);
    if (ids.Count() != entities.Count())
    {
        return NotFound();
    }
    var dtoToReturn = mapper.Map<IEnumerable<CompanyDto>>(entities);
    return Ok(dtoToReturn);
}

安全性和幂等性

安全性是指方法执行后并不会改变资源的表述
幂等性是指方法无论执行多少次都会得到同样的结果

HTTP Options

API消费者如何知道某个API是否允许被访问?
OPTION请求可以获取针对某个Web API的通信选项的信息
使用:

[HttpOptions]
public IActionResult GetCompaniesOptions()
{
    Response.Headers.Add("Allow", "GET,POST,OPTIONS");
    return Ok();
}

请求响应:
在这里插入图片描述

415 Unsupported Media Type 错误信息

这个是请求的数据格式不对或者没有,需要添加或更改Content-Type
错误信息

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ASP.NET Core WebAPI学习-4 的相关文章

随机推荐

  • gcc生成静态库与动态库(附带使用方法)

    目录 前言 1 gcc生成静态库 从使用者的角度出发 如何使用别人的静态库 方法1 方法2 直接使用静态库 2 gcc生成动态库 动态库的使用 第二种方法 与使用静态库的方法一样 解决方案 方法3 ldconfig 配置 etc ld so
  • json解析豆瓣数据

    继续上次的文章 我们找到了json的数据包 那么证明我们可以获取到他们的数据 点击Headers Request URL对应的就是json数据的url 找到url之后我们就可以开始爬虫了 import requests import jso
  • Windows和Linux混合系统通过AD域实现用户集中认证

    一 Windows AD域 1 统一认证简介 管理的Linux服务器和Windows服务器如果很多 如果都用本地用户名管理 要管理和记住几十台甚至上百台服务器的不同账号不同密码 这是很难的 但是如果所有服务器账号密码都设置一样 那又完全没有
  • Unity的C#编程教程_59_字典 Dictionary 详解及应用练习

    文章目录 C Dictionary Introduction C Dictionary Looping through Dictionary C Dictionary When to Use C Dictionary Using Dicti
  • 自举电路原理

    文章目录 一 自举电路核心原理 二 为什么要自举升压 三 简单的自举电路模型 四 自举电路在高电压栅极驱动电路中的应用 1 MOS管Q开通时 2 MOS管Q关断时 一 自举电路核心原理 电容两端电压不能突变 根据电容公式 i t C du
  • 2023智源大会议程公开丨具身智能与强化学习论坛

    6月9日 2023北京智源大会 将邀请这一领域的探索者 实践者 以及关心智能科学的每个人 共同拉开未来舞台的帷幕 你准备好了吗 与会知名嘉宾包括 图灵奖得主Yann LeCun 图灵奖得主Geoffrey Hinton OpenAI创始人S
  • 使用AddN构建tensorflow简单图例

    import tensorflow compat v1 as tf import numpy as np Define a model a computational graph Parameters for a linear model
  • 宋浩线性代数笔记(二)矩阵及其性质

    更新线性代数第二章 矩阵 本章为线代学科最核心的一章 知识点多而杂碎 务必仔细学习 重难点在于 1 矩阵的乘法运算 2 逆矩阵 伴随矩阵的求解 3 矩阵的初等变换 4 矩阵的秩 去年写的字 属实有点ugly 大家尽量看
  • R语言备忘录

    title dataclear rbase author MengKe date 2023 03 12 output html document 1 Load R packages library ggplot2 library tidyr
  • p2b网络

    把p2b的工作推广到p2rb 目的 学习目标检测 熟悉目标检测 为自己写论文打基础 我的碎碎念 真的是fuck了 自己这个东西整了这么久 还是没有整出来 从5月分我就开始了把 因为考试 因为自己喜欢玩游戏 因为我tm真的浪费了好多时间 像个
  • Vue 基于ElementUI 封装table表格组件 + pagination分页组件

    效果展示 状态页面 用户页面 Vue 源码 定义封装组件 Pagination vue
  • c 中 中文乱码_Windows平台C语言程序在控制台显示中文乱码分析及解决

    前言 初学者在Windows平台上进行C C 语言 中文 程序开发时 有时会遇到编译报错 在控制台运行时显示中文乱码的问题 本文就此类问题进行描述 展开原因分析 然后给出解决方法 本次分享内容的目录如下 基本概念 字符集 字符编码 代码页
  • vue顶部一级菜单侧边二三级菜单

    侧边栏 顶部导航
  • 电脑主板接口_如何看电脑主板 M.2 接口是支持 SATA 还是 NVMe 固态硬盘?

    飚王出品 必是精品 你的电脑能 吃鸡 吗 这句话已经成为检验一台电脑配置是否过硬的标准之一 电脑是否能 吃鸡 除了 CPU GPU 必须强悍 内存要大外 选择固态硬盘也会在安装和加载游戏时有 加持 效果 NVMe M 2 固态硬盘 固态硬盘
  • 【华清远见嵌入式培训】C基础

    Linux命令基础 1 Linux文件类型 bsp lcd 七种 b 块 block 设备文件 存储设备 硬盘 SD卡 dev sd s 套接字 socket 文件 网络编程 p 管道 pipe 文件 I O编程 普通文件 c文件 h文件
  • 遇见狂神说,JavaSE基础总结(完整思维导图)

    转自 狂神学JAVA 基础篇 JavaSE Xmind总结 思维导图 Bilibili源视频 狂神说Java JavaSE阶段回顾总结 学习 思维导图与实际知识点结合
  • 虚拟机桥接模式连不上网问题(非桥接网卡原因)

    虚拟机和宿主机可以互相ping通 但是ping www baidu com不能成功 常见的原因是桥接到的网卡不对 网上搜也是大部分搜到这种原因解决办法 参见解决VMware虚拟机桥接模式上不了网 我遇到的问题不是这个 是因为我没有配置网关
  • c++赋值(赋值为函数返回值)语句的返回值问题

    不知道为什么 之前一直脑子里有一个误解 赋值语句的返回值应该是1 成功赋值 或0 赋值失败 今天其实在学linux的时候突然揣摩了一下代码才发现这个问题 if dir opendir home ljz Desktop NULL opendi
  • ABB ACS 510 1.5-5.5kw驱动板图纸 PDF格式

    ABB ACS 510 1 5 5 5kw驱动板图纸 PDF格式 编号 3416654015862907LHY782717783
  • ASP.NET Core WebAPI学习-4

    ASP NET Core WebAPI学习 1 ASP NET Core WebAPI学习 2 ASP NET Core WebAPI学习 3 ASP NET Core WebAPI学习 4 ASP NET Core WebAPI学习 5