当前位置: 首页 > news >正文

如何将一个 .NET 对象序列化为 HTTP GET 的请求字符串

HTTP GET 请求时携带的参数直接在 URL 中,形式如 ?key1=value&key2=value&key3=value。如果是 POST 请求时,我们可以使用一些库序列化为 json 格式作为 BODY 发送,那么 GET 请求呢?有可以直接将其序列化为 HTTP GET 请求的 query 字符串的吗?


本文内容

    • HTTP GET 请求
    • 库?
    • 方法

HTTP GET 请求

一个典型的 HTTP GET 请求带参数的话大概是这样的:

https://s.blog.walterlv.com/api/example?key1=value&key2=value&key3=value

于是我们将一个类型序列化为后面的参数:

[DataContract]
public class Foo
{
    [DataMember(Name = "key1")]
    public string? Key1 { get; set; }

    [DataMember(Name = "key2")]
    public string? Key2 { get; set; }

    [DataMember(Name = "key3")]
    public string? Key3 { get; set; }
}

库?

可能是这个需求太简单了,所以并没有找到单独的库。所以我就写了一个源代码包放到了 nuget.org 上。

在这里下载源代码包:

  • Walterlv.Web.Source

你不需要担心引入额外的依赖,因为这是一个源代码包。关于源代码包不引入额外依赖 dll 的原理,可以参见:

  • .NET 将多个程序集合并成单一程序集的 4+3 种方法 - walterlv

方法

我们需要做的是,将一个对象序列化为 query 字符串。假设这个对象的局部变量名称是 query,于是我们需要:

  1. 取得此对象所有可获取值的属性
    • query.GetType().GetProperties()
  2. 获取此属性值的方法
    • property.GetValue(query, null)
  3. 将属性和值拼接起来
    • string.Join("&", properties)

然而真实场景可能比这个稍微复杂一点:

  1. 我们需要像 Newtonsoft.Json 一样,对于标记了 DataContract 的类,按照 DataMember 来序列化
  2. URL 中的值需要进行转义

所以,我写出了下面的方法:

var isContractedType = query.GetType().IsDefined(typeof(DataContractAttribute));
var properties = from property in query.GetType().GetProperties()
                    where property.CanRead && (isContractedType ? property.IsDefined(typeof(DataMemberAttribute)) : true)
                    let memberName = isContractedType ? property.GetCustomAttribute<DataMemberAttribute>().Name : property.Name
                    let value = property.GetValue(query, null)
                    where value != null && !string.IsNullOrWhiteSpace(value.ToString())
                    select memberName + "=" + HttpUtility.UrlEncode(value.ToString());
var queryString = string.Join("&", properties);
return string.IsNullOrWhiteSpace(queryString) ? "" : prefix + queryString;

完整的代码如下:

using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Web;

namespace Walterlv.Web.Core
{
    internal class QueryString
    {
        [return: NotNullIfNotNull("query")]
        public static string? Serialize(object? query, string? prefix = "?")
        {
            if (query is null)
            {
                return null;
            }

            var isContractedType = query.GetType().IsDefined(typeof(DataContractAttribute));
            var properties = from property in query.GetType().GetProperties()
                             where property.CanRead && (isContractedType ? property.IsDefined(typeof(DataMemberAttribute)) : true)
                             let memberName = isContractedType ? property.GetCustomAttribute<DataMemberAttribute>().Name : property.Name
                             let value = property.GetValue(query, null)
                             where value != null && !string.IsNullOrWhiteSpace(value.ToString())
                             select memberName + "=" + HttpUtility.UrlEncode(value.ToString());
            var queryString = string.Join("&", properties);
            return string.IsNullOrWhiteSpace(queryString) ? "" : prefix + queryString;
        }
    }
}

你可能会遇到 [return: NotNullIfNotNull("query")] 这一行编译不通过的情况,这个是 C# 8.0 带的可空引用类型所需要的契约类。

你可以将它删除,或者安装我的另一个 NuGet 包来获得更多可空引用类型契约的支持,详见:

  • C# 8.0 的可空引用类型,不止是加个问号哦!你还有很多种不同的可空玩法 - walterlv

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • 屏幕边缘上有趣的 1 个像素,看不见、摸不到
  • 在 MSBuild 编译过程中操作文件和文件夹(检查存在/创建文件夹/读写文件/移动文件/复制文件/删除文件夹)
  • 在 WPF 程序中应用 Windows 10 真•亚克力效果
  • 推荐 .NET/C# 开发者安装的几款代码分析插件或对应的代码分析 NuGet 包
  • 在 HTML 超链接上添加可交互的 ToolTip
  • 在移动端打开 Google 的网页快照
  • 为自己搭建的博客添加可切换的暗色和亮色主题
  • 如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target(附各种自带的 Task)
  • 让你编写的控件库在 XAML 中有一个统一的漂亮的命名空间(xmlns)和命名空间前缀
  • Sdk 风格的 csproj 对 WPF/UWP 支持不太好?有第三方 SDK 可以用!MSBuild.Sdk.Extras
  • 为博客或个人站点的 Markdown 添加 LaTeX 公式支持
  • 如何让 .NET Core 命令行程序接受密码的输入而不显示密码明文
  • 如何编写 WPF 的标记扩展 MarkupExtension,即便在 ControlTemplate/DataTemplate 中也能生效
  • PasswordVault —— 在 UWP 应用中安全地保存密码
  • 如何在 MSBuild Target(Exec)中报告编译错误和编译警告
  • 【Leetcode】101. 对称二叉树
  • (三)从jvm层面了解线程的启动和停止
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 【附node操作实例】redis简明入门系列—字符串类型
  • Angular4 模板式表单用法以及验证
  • isset在php5.6-和php7.0+的一些差异
  • JavaScript 奇技淫巧
  • JavaScript标准库系列——Math对象和Date对象(二)
  • log4j2输出到kafka
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • nfs客户端进程变D,延伸linux的lock
  • python 学习笔记 - Queue Pipes,进程间通讯
  • storm drpc实例
  • 基于游标的分页接口实现
  • 开源地图数据可视化库——mapnik
  • 你真的知道 == 和 equals 的区别吗?
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • ​批处理文件中的errorlevel用法
  • #{} 和 ${}区别
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (+4)2.2UML建模图
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (arch)linux 转换文件编码格式
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (CPU/GPU)粒子继承贴图颜色发射
  • (Python第六天)文件处理
  • (Qt) 默认QtWidget应用包含什么?
  • (备忘)Java Map 遍历
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)计算机毕业设计SSM在线影视购票系统
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • ./include/caffe/util/cudnn.hpp: In function ‘const char* cudnnGetErrorString(cudnnStatus_t)’: ./incl