ASP.NET Core OData 9 正式发布
我们很高兴地宣布,ASP.NET Core OData 9 已正式发布,并在 NuGet 上提供:
此版本的主要亮点是将 OData .NET 依赖项更新到 8.x 主版本。 通过更新依赖项,我们能够利用 Microsoft.OData.Core 8.x 和 Microsoft.OData.Edm 8.x 版本中引入的改进和新功能。
ASP.NET Core OData 9 版本将仅支持 .NET 8 或更高版本。
OData .NET 8 官方发布公告解决了该版本中引入的主要更改。建议阅读这篇文章以熟悉这些变化。
在本文中,我们将探讨其中一些更改如何影响 ASP.NET Core OData 库,以及如何在可能的情况下切换旧行为。
请求和响应负载中的字符编码
在 OData .NET 8 中,我们引入了一个新的 JSON 编写器,它在后台使用 .NET Utf8JsonWriter 来编写请求和响应有效负载。新的 JSON 编写器速度明显更快,并且是 ASP.NET Core OData 9 中的默认编写器。
您可能会观察到输出负载上的字符编码存在差异。我们将在以下各节中介绍这些差异。
编码的字符子集
在其默认配置中,新的 JSON 编写器不会像旧版 (OData .NET 7) 那样对大量字符进行编码。
默认情况下,旧版 JSON 编写器对所有整数值小于 32 且大于 127 的字符进行编码,基本上都是非 ASCII 字符。这计算出大约 65440 个字符!可以通过将 option 传递给默认的 JSON 编写器工厂构造函数来覆盖旧版 JSON 编写器的默认配置。使用这种替代配置,将对大大减少的字符子集进行编码。ODataStringEscapeOption.EscapeOnlyControls
新的 JSON 编写器使用默认情况下配置了 encoder 选项的基础。编码的结果字符集要小得多。Utf8JsonWriter
JavaScriptEncoder.UnsafeRelaxedJsonEscaping
下面是一个插图 – 以 OData 服务的代码示例形式,演示了使用新的 JSON 编写器时输出有效负载的外观:
// Model namespace Ver900Sample.Models { public class Order { public int Id { get; set; } public decimal Amount { get; set; } public string Note { get; set; } } } // Controller using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Results; using Microsoft.AspNetCore.OData.Routing.Controllers; using Ver900Sample.Models; namespace Ver900Sample.Controllers { public class OrdersController : ODataController { private static readonly List<Order> orders = new List<Order> { new Order { Id = 1, Amount = 130m, Note = "a - z, α - Ω" }, new Order { Id = 2, Amount = 170.50m, Note = "😀 🐂 🐕" } }; [EnableQuery] public SingleResult<Order> Get(int key) { return SingleResult.Create(orders.AsQueryable().Where(d => d.Id == key)); } } } // Service configuration using Ver900Sample.Models; using Microsoft.AspNetCore.OData; using Microsoft.OData.ModelBuilder; var builder = WebApplication.CreateBuilder(args); var modelBuilder = new ODataConventionModelBuilder(); modelBuilder.EntitySet<Order>("Orders"); builder.Services.AddControllers().AddOData( options => options.EnableQueryFeatures().AddRouteComponents( model: modelBuilder.GetEdmModel())); var app = builder.Build(); app.UseRouting(); app.MapControllers(); app.Run();
您可以使用该工具查询此服务的订单 1 – 浏览器可能不是此图的好选择,因为它会智能解码编码的字符。curl
curl http://localhost:5090/Orders(1)
注意:5090 是计算机分配的随机端口,因计算机而异。
以下是您将观察到的结果:
{ "@odata.context": "http://localhost:5090/$metadata#Orders/$entity", "Id": 1, "Amount": 130, "Note": "a - z, α - Ω" }
在上述情况下,新的 JSON 编写器不会对 和非 ASCII 字符进行编码。α
Ω
如果在您的方案中保留旧版 JSON 编写器的行为很重要,则可以通过使用依赖项注入将新的 JSON 编写器替换为旧版 JSON 编写器来实现此目的。您可以通过修改上述代码片段中的方法调用来执行此操作,如下所示:AddOData
builder.Services.AddControllers().AddOData( options => options.EnableQueryFeatures().AddRouteComponents( routePrefix: string.Empty, model: modelBuilder.GetEdmModel(), configureServices: (services) => { services.AddScoped<Microsoft.OData.Json.IJsonWriterFactory>( sp => new Microsoft.OData.Json.ODataJsonWriterFactory()); }));
注意:该类是以前在 OData .NET 7 中命名的类。我们认为名称中包含“Default”会使歧义永久存在,因为它不是 OData .NET 8 中的默认 JSON 编写器工厂。ODataJsonWriterFactory
DefaultJsonWriterFactory
如果您在上述更改后查询订单 1,响应有效负载将如下所示:
{ "@odata.context": "http://localhost:5090/$metadata#Orders/$entity", "Id": 1, "Amount": 130, "Note": "a - z, \u03b1 - \u03a9" }
在上述情况下,将对非 ASCII 字符进行编码。
还支持更严格的 JavaScript 编码器。您可以配置您的服务以使用该编码器,如下所示:Utf8JsonWriter
builder.Services.AddControllers().AddOData( options => options.EnableQueryFeatures().AddRouteComponents( routePrefix: string.Empty, model: modelBuilder.GetEdmModel(), configureServices: (services) => { services.AddScoped<Microsoft.OData.Json.IJsonWriterFactory>( sp => new Microsoft.OData.Json.ODataUtf8JsonWriterFactory( System.Text.Encodings.Web.JavaScriptEncoder.Default)); }));
如果您在上述更改后查询订单 1,响应有效负载将如下所示:
{ "@odata.context": "http://localhost:5090/$metadata#Orders/$entity", "Id": 1, "Amount": 130, "Note": "a - z, \u03B1 - \u03A9" }
上述响应有效负载看起来类似于旧版 JSON 编写器的输出。
但是,请务必注意,使用更严格的 JavaScript 编码器配置的编码器并不是旧版 JSON 编写器在编码字符方面的镜像。Utf8JsonWriter
要关闭循环,下面介绍如何将服务配置为使用具有不太严格编码选项的旧版 JSON 编写器:
builder.Services.AddControllers().AddOData( options => options.EnableQueryFeatures().AddRouteComponents( routePrefix: string.Empty, model: modelBuilder.GetEdmModel(), configureServices: (services) => { services.AddScoped<Microsoft.OData.Json.IJsonWriterFactory>( sp => new Microsoft.OData.Json.ODataJsonWriterFactory( Microsoft.OData.Json.ODataStringEscapeOption.EscapeOnlyControls)); }));
选择配置选项是因为它是 ASP.NET Core 8.0 中配置的默认选项。JavaScriptEncoder.UnsafeRelaxedJsonEscaping
Unicode 码位的大写字母
新的 JSON 编写器在编码输出中使用大写字母,而旧版 JSON 编写器使用小写字母。这两个输出都是合法的,客户端反序列化任何一种格式都应该没有问题。
使用上一节中的示例服务并配置了新的 JSON 编写器,您可以按如下方式查询订单 2:
curl http://localhost:5090/Orders(2)
以下是您将观察到的结果:
{ "@odata.context": "http://localhost:5090/$metadata#Orders/$entity", "Id": 2, "Amount": 170.50, "Note": "\uD83D\uDE00 \uD83D\uDC02 \uD83D\uDC15" }
请注意,Unicode 字符的编码值为大写。
如果您在配置了旧版 JSON 编写器的情况下查询相同的顺序 2,则响应有效负载如下所示:
{ "@odata.context": "http://localhost:5090/$metadata#Orders/$entity", "Id": 2, "Amount": 170.50, "Note": "\ud83d\ude00 \ud83d\udc02 \ud83d\udc15" }
如果所描述的字符编码更改在您的方案中是一个交易破坏者,请将新的 JSON 编写器替换为旧版 JSON 编写器,如图所示。
对依赖项注入的更改
我们进行了重大更改,以适应 OData .NET 8 中的依赖关系注入重构。我们摆脱了非标准的依赖注入工件(例如),转而使用 .NET 框架依赖注入抽象。IContainerBuilder
具体而言,对 OData 核心库中方法的更改使配置 、 和如下所示成为可能:AddODataDefaultServices
ODataReaderSettings
ODataMessageWriterSettings
ODataUriParserSettings
builder.Services.AddControllers().AddOData( options => options.EnableQueryFeatures().AddRouteComponents( routePrefix: string.Empty, model: modelBuilder.GetEdmModel(), configureServices: (services) => { services.AddDefaultODataServices( odataVersion: Microsoft.OData.ODataVersion.V4, configureReaderAction: (messageReaderSettings) => { // Relevant changes to the ODataMessageReaderSettings instance here }, configureWriterAction: (messageWriterSettings) => { // Relevant changes to the ODataMessageWriterSettings instance here }, configureUriParserAction: (uriParserSettings) => { // // Relevant changes to the ODataUriParserSettings instance here }); }));
这为开发人员在操作这些特定设置时提供了更大的灵活性。
该参数还使注入任何所需的依赖项变得简单明了:IServiceCollection
builder.Services.AddControllers().AddOData( options => options.EnableQueryFeatures().AddRouteComponents( routePrefix: string.Empty, model: modelBuilder.GetEdmModel(), configureServices: (services) => { services.AddSingleton<Microsoft.OData.ODataPayloadValueConverter>( sp => new CustomODataPayloadValueConverter()); })); // ... public class CustomODataPayloadValueConverter : Microsoft.OData.ODataPayloadValueConverter { public override object ConvertToPayloadValue(object value, IEdmTypeReference edmTypeReference) { if (edmTypeReference.PrimitiveKind() == EdmPrimitiveTypeKind.DateTimeOffset) { var dateTimeOffset = (DateTimeOffset)value; return dateTimeOffset.ToString("yyyy-MM-dd'T'HH:mm:ss.fffffffzzz"); } return base.ConvertToPayloadValue(value, edmTypeReference); } }
向后兼容性标志
OData .NET 8 的一个主要目标是确保迁移到新版本的客户能够输出与在 OData .NET 7 中相同的响应负载。在进行更改以更好地与 OData 标准保持一致的地方,添加了兼容性标志,以便能够切换旧行为。
我们为与 OData 标准保持一致而进行的更改的一个示例是,在服务元数据负载中编写十进制属性的属性,以及为空间属性编写属性。Scale
SRID
如果您查询上一节中示例服务的服务元数据终端节点 (http://localhost:5090/$metadata),您将获得以下有效负载:
<?xml version="1.0" encoding="utf-8"?> <edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"> <edmx:DataServices> <Schema Namespace="Ver900Sample.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm"> <EntityType Name="Order"> <Key> <PropertyRef Name="Id" /> </Key> <Property Name="Id" Type="Edm.Int32" Nullable="false" /> <Property Name="Amount" Type="Edm.Decimal" Nullable="false" Scale="variable" /> <Property Name="Note" Type="Edm.String" Nullable="false" /> </EntityType> </Schema> <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm"> <EntityContainer Name="Container"> <EntitySet Name="Orders" EntityType="Ver900Sample.Models.Order" /> </EntityContainer> </Schema> </edmx:DataServices> </edmx:Edmx>
在 OData .NET 7 中,该属性将写为 – 带有大写的“V”。与 OData 标准的这种偏差在 OData .NET 8 中已修复。该属性现在写入 ,如上面的有效负载所示。Scale
Scale="Variable"
Scale
Scale="variable"
如果在您的方案中维护 OData .NET 7 行为很重要,您可以通过切换功能标志来实现这一点,如下所示:
builder.Services.AddControllers().AddOData( options => options.EnableQueryFeatures().AddRouteComponents( routePrefix: string.Empty, model: modelBuilder.GetEdmModel(), configureServices: (services) => { services.AddDefaultODataServices( odataVersion: Microsoft.OData.ODataVersion.V4, configureReaderAction: null, configureWriterAction: (messageWriterSettings) => { messageWriterSettings.LibraryCompatibility |= Microsoft.OData.ODataLibraryCompatibility.UseLegacyVariableCasing; }, configureUriParserAction: null); }));
切换标志后,将写入该属性,以便与 OData .NET 7 兼容。ODataLibraryCompability.UseLegacyVariableCasing
Scale
Scale="Variable"
您可以根据需要组合任意数量的兼容性标志来实现所需的行为。
若要切换所有兼容性标志以实现 OData .NET 7 兼容性,可以使用方便的标志,如下所示:ODataLibraryCompatibility.Version7
builder.Services.AddControllers().AddOData( options => options.EnableQueryFeatures().AddRouteComponents( routePrefix: string.Empty, model: modelBuilder.GetEdmModel(), configureServices: (services) => { services.AddDefaultODataServices( odataVersion: Microsoft.OData.ODataVersion.V4, configureReaderAction: null, configureWriterAction: (messageWriterSettings) => { messageWriterSettings.LibraryCompatibility |= Microsoft.OData.ODataLibraryCompatibility.Version7; }, configureUriParserAction: null); }));
结论
我们邀请您试用 ASP.NET Core OData 9 并与我们分享您的反馈。感谢您对 OData 生态系统的持续支持和贡献。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
2023-10-09 JavaScript获取浏览器的显示区域大小测试