惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
J
Java Code Geeks
雷峰网
雷峰网
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
云风的 BLOG
云风的 BLOG
V
Visual Studio Blog
V
Vulnerabilities – Threatpost
S
Securelist
The Hacker News
The Hacker News
The Register - Security
The Register - Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Help Net Security
Help Net Security
G
Google Developers Blog
Hugging Face - Blog
Hugging Face - Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
M
MIT News - Artificial intelligence
AI
AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Schneier on Security
Schneier on Security
N
Netflix TechBlog - Medium
T
The Blog of Author Tim Ferriss
Google DeepMind News
Google DeepMind News
Hacker News - Newest:
Hacker News - Newest: "LLM"
H
Hacker News: Front Page
博客园 - 司徒正美
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
B
Blog
Microsoft Azure Blog
Microsoft Azure Blog
大猫的无限游戏
大猫的无限游戏
Security Latest
Security Latest
Engineering at Meta
Engineering at Meta
N
News and Events Feed by Topic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Threat Research - Cisco Blogs
U
Unit 42
V
V2EX
V2EX - 技术
V2EX - 技术
L
LINUX DO - 最新话题
aimingoo的专栏
aimingoo的专栏
Microsoft Security Blog
Microsoft Security Blog
Recorded Future
Recorded Future
P
Privacy & Cybersecurity Law Blog
美团技术团队
小众软件
小众软件
F
Fortinet All Blogs

博客园 - cdboy

EF Core 中原生SQL、存储过程、视图的使用 EF Core DBFirst 和Code First小结 软件自动发布自动化之配置文件修改 Logstash日志搜集 Windows Service插件服务开源 Linq通用分页数据查询方法 .Net 通用配置文件读取方法 Asp.net 主题中CSS文件的缓存问题 Asp .net 4.0 中ViewStatus 使用 RssTookit使用小结 Windows Live Writer 分享到插件 Windows Resx资源文件编辑工具 IIS 7 中设置文件上传大小限制设置方法 多语言资源文件帮助 转帖:正则表达式的与或非 vs2010使用PostSharp 1.5 window service 插件服务插件开发 插件式服务架构 多语言资源工具之制作类库资源文件
.Net8新特性
cdboy · 2023-11-25 · via 博客园 - cdboy

本文整理.Net8新特性的使用方法。
当前包括:

  • Route ShortCircuit
  • Exception Throw Helper
  • HttpLoggingMiddleware 的改进
  • C# 12 中的 InlineArray 特性

Route ShortCircuit

有些请求,如浏览器会自动请求 favicon.ico,这些请求即使很简单,往往也会完整地运行中间件管道,但实际上可能并不需要,在 .NET 8 中引入了一个 Route ShortCircuit 的功能,也就是路由短路,可以在处理结束之后马上中断请求,不再执行后面的中间件了,这样会使得这样的路由或者 API 更加高效。

在 route 后添加 ShortCircut() 来启用,使用方法如下:

var builder = WebApplication.CreateSlimBuilder(args);

var app = builder.Build();
app.Use(async (HttpContext context, RequestDelegate next) =>
{
    context.Response.Headers["Value"] = "123";
    await next(context);
});
app.MapGet("/", () => "Hello .NET 8!");

// ShortCircuit
app.MapGet("/short-circuit", () => "Short circuiting!").ShortCircuit();
app.MapGet("/short-circuit-status", () => "Short circuiting!")
            .ShortCircuit(401);
// MapShortCircuit
app.MapShortCircuit(404, "robots.txt", "favicon.ico");
app.MapShortCircuit(403, "admin");

await app.RunAsync();

Exception Throw Helper

在 .NET 6 中,引入了一个 ArgumentNullException.ThrowIfNull(object? argument, string? paramName = default) 的方法,在 .NET 7/8 中引入了更多的支持,我们可以在代码里使用这些 exception helper 来简化一些代码。

常用的 Argument exception:

ArgumentNullExceptionSample(null);

ArgumentExceptionThrowIfNullOrEmptySample(null);
ArgumentExceptionThrowIfNullOrEmptySample(string.Empty);

ArgumentExceptionThrowIfNullOrWhiteSpaceSample(null);
ArgumentExceptionThrowIfNullOrWhiteSpaceSample(string.Empty);
ArgumentExceptionThrowIfNullOrWhiteSpaceSample(" ");

public static void ArgumentNullExceptionSample(string? value)
{
    InvokeHelper.TryInvoke(() => ArgumentNullException.ThrowIfNull(value));
}

public static void ArgumentExceptionThrowIfNullOrEmptySample(string? value)
{
    InvokeHelper.TryInvoke(() => ArgumentException.ThrowIfNullOrEmpty(value));
}

public static void ArgumentExceptionThrowIfNullOrWhiteSpaceSample(string? value)
{
    InvokeHelper.TryInvoke(() => ArgumentException.ThrowIfNullOrWhiteSpace(value));
}

ArgumentNullException.ThrowIfNull(object? obj, string? paramName = default) 是 .NET 6 开始支持的

在 .NET 7 里支持了指针的判断 ArgumentNullException.ThrowIfNull(void* argument, string? paramName = default)(not CLS-compliant)

.NET 7 还引入了 ArgumentException.ThrowIfNullOrEmpty(string? value, string? paramName = default), 增加判断空字符串的场景

.NET 8 引入了 ArgumentException.ThrowIfNullOrWhiteSpace(string? value, string? paramName = default) 增强了判断空字符串的场景

HttpLoggingMiddleware 的改进

.NET 6 开始引入了一个 http logging 的中间件,我们可以借助于 http logging 的中间件记录请求和响应的信息,但是扩展性不是很强,在 .NET 8 版本中进行了一些优化,引入了一些新的配置和 HttpLoggingInterceptor 使得它更加容易扩展了

HttpLoggingFields 中新增了一个 Duration 枚举值,会记录请求处理的耗时

在 HttpLoggingOptions 中增加了一个 CombineLogs 的配置,默认是 false,默认 request/response/duration 的 log 都是分开的,配置为 true 之后就会合并成一条日志。

HttpLoggingInterceptor

.NET 8 还引入了 IHttpLoggingInterceptor,借助于此可以更好的扩展 http logging

可以根据 Request 或者 Response 信息来动态地调整要记录的 field 或者动态调整 RequestBodyLogLimit/ResponseBodyLogLimit

来看一个 HttpLoggingInterceptor 示例:

file sealed class MyHttpLoggingInterceptor: IHttpLoggingInterceptor
{
    public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
    {
        if (logContext.HttpContext.Request.Path.Value?.StartsWith("/req-") == true)
        {
            logContext.LoggingFields = HttpLoggingFields.ResponsePropertiesAndHeaders;
            logContext.AddParameter("req-path", logContext.HttpContext.Request.Path.Value);
        }
        
        return ValueTask.CompletedTask;
    }

    public ValueTask OnResponseAsync(HttpLoggingInterceptorContext logContext)
    {
        if (logContext.HttpContext is { Response.StatusCode: >=200 and < 300, Request.Path.Value: "/hello" })
        {
            logContext.TryDisable(HttpLoggingFields.All);
        }
        return ValueTask.CompletedTask;
    }
}

使用示例如下,使用 AddHttpLoggingInterceptor() 来注册:

var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddControllers();
builder.Services.AddHttpLogging(options =>
{
    options.LoggingFields = HttpLoggingFields.All;
    options.CombineLogs = true;
});
builder.Services.AddHttpLoggingInterceptor<MyHttpLoggingInterceptor>();

var app = builder.Build();
app.UseHttpLogging();
app.MapGet("/hello", () => "Hello");
app.MapGet("/crash", () => Results.BadRequest());
app.MapGet("/req-intercept", () => "Hello .NET 8");
app.MapControllers();
await app.RunAsync();

可以看到每个请求的 log 输出的结果都有所不同,第一个请求虽然我们设置了 ogContext.TryDisable(HttpLoggingFields.All) 但是还是有输出结果这是因为 httpLogging 目前的实现就是这样,在 Response 里处理的时候 request 信息已经被记录好了,详细可以参考 http logging middleware 的 实现

如果想要完全 disable 需要在 OnRequestAsync 方法里处理

public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
{
    if ("/no-log".Equals(logContext.HttpContext.Request.Path.Value, StringComparison.OrdinalIgnoreCase))
    {
        logContext.LoggingFields = HttpLoggingFields.None;
    }
    //
    return ValueTask.CompletedTask;
}

这样请求就不会有日志打印了

最后一个 req-intercept 在 request 的处理中设置了 ResponsePropertiesAndHeaders 并且加了一个自定义的 Parameter 从输出结果可以看到有输出到日志

More
大家可以自己尝试一下,比之前会好用一些,但是觉得还是有所欠缺

比如日志级别目前还都是 Information 不能动态的改变日志级别

另外就是前面提到的即使使用 CombineLogs 在 response 中设置为 HttpLoggingFields.None 时,依然会记录 request 信息,希望后面还会继续优化一下

C# 12 中的 InlineArray 特性

C# 12 引入了一个 InlineArray 特性,利用这一特性,我们可以更方便地类数组的结构体,可以代替原来要使用非安全代码的 fixed size buffer

[System.Runtime.CompilerServices.InlineArray(10)]
file struct MyArray
{
    // required
    private int _element;
}

使用 InlineArray 需要指定 size,也就是 array 的长度,并且我们需要声明一个字段

使用示例如下:

var arr = new MyArray();
for (var i = 0; i < 10; i++)
{
    arr[i] = i;
}
foreach (var i in arr)
{
    Console.Write(i);
    Console.Write(",");
}

Console.WriteLine();

ReadOnlySpan<int> span = arr;
foreach (var i in span)
{
    Console.Write(i);
    Console.Write(",");
}

Console.WriteLine();

foreach (var i in arr[^2..])
{
    Console.Write(i);
    Console.Write(",");
}
Console.WriteLine();

Console.WriteLine(arr[^1]);

// error CS0021: Cannot apply indexing with [] to an expression of type 'MyArray'
// if (arr is [0,1,..])
//     Console.WriteLine("StartsWith 0, 1");

if (span is [0,1,..])
    Console.WriteLine("StartsWith 0, 1");

// error CS9174: Cannot initialize type 'MyArray' with a collection expression because the type is not constructible.
// arr = [1, 2, 3, 4, 5];

span = [1,2,3,4,5];
foreach (var item in span)
{
    Console.Write(item);
    Console.Write(",");
}

运行结果:

0,1,2,3,4,5,6,7,8,9,
0,1,2,3,4,5,6,7,8,9,
8,9,
9
StartsWith 0, 1
1,2,3,4,5,

从这个示例可以看得出来,我们可以像使用数组一样使用,同时我们可以直接隐式转换成 Span 和 ReadOnlySpan,
并且可以使用 Index 和 Range 操作符,但是目前暂时不能直接使用集合表达式和 list pattern,但是我们可以转成 span 之后再使用
文章来源