当前位置:

首页 > 编程开发 > .NET  i18n 原理、实现一个 i18n 框架的过程详解

.NET  i18n 原理、实现一个 i18n 框架的过程详解

基于ASP.NETCore实现的Maomi.I18n多语言框架,通过键值对管理语言资源,支持按项目目录组织JSON文件,编译后统一合并。框架利用RequestLocalizationMiddleware自动解析请求语言,适用于控制台、Web和WPF等场景,实现通用国际化方案。

本文目录

    随着业务的国际化,软件产品需要支持多种语言,这已经成了基本需求——用户选择什么语言,界面就展示什么语言。ASP.NET Core 或 ABP 这类框架虽然都提供了多语言方案,配置方式各不相同,但底层思路其实一样:通过键值对来管理,开发者为每个 key 提供不同语言的值,框架根据请求上下文自动匹配。说白了,就是做个翻译映射表。

    今天我们就来聊聊如何基于 ASP.NET Core 实现一个实用的 i18n 多语言框架。最终成品叫 Maomi.I18n,它能同时跑在控制台、ASP.NET Core、WPF 等项目里,还支持自定义多语言资源,算是一套通用方案。

    什么是 i18n?

    i18n 是 "internationalization"(国际化)的缩写。名字挺有意思——首字母 i 和末字母 n 之间正好隔着 18 个字母,所以就叫 i18n。简单说,i18n = 国际化,就是让应用能适应不同语言和地区的技术。

    类似的缩写还有 L10n (localization,本地化),L 和 n 之间有 10 个字母。

    i18n vs L10n 的区别

    概念英文含义示例
    国际化i18n开发时让程序支持多语言的能力使用资源文件、支持 Unicode、处理不同日期格式
    本地化L10n针对特定语言/地区进行适配翻译文本、使用本地货币符号、适配当地日期格式

    类比一下:

    • i18n = 手机充电口设计成 Type-C(通用接口)
    • L10n = 给手机配一个当地规格的充电头

    体验 Maomi.I18n

    在动手写框架之前,先来感受一下最终成品 Maomi.I18n 怎么用。毕竟,先知道它有多好用,才有动力研究它怎么实现的。

    控制台示例

    如果要用 json 文件存储语言包,就需要按项目名称创建目录。我们拿一个实际例子走一遍流程。

    创建一个 Demo5.Lib 项目,引入 Maomi.I18n 框架。然后在 Demo5.Lib 和 Console 两个项目下都添加 i18n 目录和对应的多语言资源文件。

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    在 i18n/Demo5.Lib 目录中创建 en-US.json、zh-CN.json 文件。

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    两个文件的内容都设置为:

    {  "test": "lib"}

    右键修改 en-US.json、zh-CN.json 属性,设置生成操作为内容、始终复制到输出目录。

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    然后创建一个 Demo5.Console 项目,引用 Demo5.Lib。

    在 i18n/Demo5.Console 目录中创建 en-US.json、zh-CN.json 文件。

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    两个文件都设置内容为:

    {  "test": "console"}

    最后得到的目录结构如下:

    ├─Demo5.Console│  │  Demo5.Console.csproj│  │  Program.cs│  ││  ├─i18n│  │  └─Demo5.Console│  │          en-US.json│  │          zh-CN.json│├─Demo5.Lib│  │  Demo5.Lib.csproj│  │  Extensions.cs│  ││  ├─i18n│  │  └─Demo5.Lib│  │          en-US.json│  │          zh-CN.json

    在 Demo5.Console 的 Program 中使用 IStringLocalizer 来获取 key 在不同语言下的值。

    var ioc = new ServiceCollection();
    ioc.AddI18n("zh-CN");
    ioc.AddI18nResource(options =>{options.ParseDirectory("i18n");});
    ioc.AddLib();
    var services = ioc.BuildServiceProvider();
    // 手动设置当前请求语言
    using (var c = new I18nScope("en-US"))
    {
        var l1 = services.GetRequiredService>();
        var l2 = services.GetRequiredService>();
        var s1 = l1["test"];
        var s2 = l2["test"];
        Console.WriteLine(s1);
        Console.WriteLine(s2);
    }

    编译 Demo5.Console ,打开 bin/Debug/net8.0 目录,在 i18n 目录下可以看到如下文件结构:

    .
    ├── Demo5.Console
    │   ├── en-US.json
    │   └── zh-CN.json
    └── Demo5.Lib
        ├── en-US.json
        └── zh-CN.json

    Maomi.I18n 的原理其实很简单:每个项目都有自己的多语言文件,编译后所有文件都合并到统一的 i18n 目录里管理,每个子目录名就是项目名,一目了然。使用 IStringLocalizer 时,框架会自动从 T 类型所在的项目名称目录下加载对应的 json 文件。

    单个项目管理

    上一个例子是每个项目各自管自己的资源文件。那如果整个解决方案共用一套语言文件呢?当然也可以,不需要按目录划分。

    直接导入多语言资源文件:

    ioc.AddI18nResource(options =>
    {
        options.AddJsonFile("zh-CN", "i18n/zh-CN.json");
        options.AddJsonFile("en-US", "i18n/en-US.json");
    });

    或者自动扫描目录,让 json 文件名自动成为语言名称:

    ioc.AddI18nResource(options =>
    {
        options.AddJsonDirectory("i18n");
    });

    使用时直接注入 IStringLocalizer(不需要泛型版本):

    // 手动设置当前请求语言
    using (var c = new I18nScope("en-US"))
    {
        var l1 = services.GetRequiredService();
        var l2 = services.GetRequiredService();
        var s1 = l1["test"];
        var s2 = l2["test"];
        Console.WriteLine(s1);
        Console.WriteLine(s2);
    }

    如何设置当前语言

    设置当前上下文语言,最直接的方式就是改当前线程文化:

    CultureInfo.CurrentCulture = new CultureInfo("zh-CN");

    另一种方法更灵活——开发者可以自定义如何解析当前程序的多语言上下文,比如从请求头或数据库读取:

    public class MyI18nContext : I18nContext
    {
        public MyI18nContext()
        {
            base.Culture = ...
        }
        public void Set(CultureInfo cultureInfo)
        {
            base.Culture = cultureInfo;
        }
    }
    builder.Services.AddScoped();

    Web 示例

    创建一个 Api 项目,取名 Demo5.Api ,引入 Maomi.I18n.AspNetCore 。

    在项目中新建 i18n/Demo5.Api 目录,然后创建两个 json 文件。

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    zh-CN.json 文件内容:

    {
      "购物车": {
        "商品名称": "商品名称",
        "加入时间": "加入时间",
        "清理失效商品": "清理失效商品"
      },
      "会员等级": {
        "用户名": "用户名",
        "积分": "积分:{0}",
        "等级": "等级"
      }
    }

    en-US.json 文件内容:

    {
      "购物车": {
        "商品名称": "Product name",
        "加入时间": "Join date",
        "清理失效商品": "Cleaning up failures"
      },
      "会员等级": {
        "用户名": "Username",
        "积分": "Member points:{0}",
        "等级": "Level"
      }
    }

    Maomi.I18n 框架会扫描程序集目录的 json 文件,解析后以键值对形式存进内存。key 的格式与 IConfiguration 一致,支持嵌套路径(比如 ["购物车:商品名称"]),还支持字符串插值(如 "会员等级:积分" 带 {0} 参数)。

    使用 Maomi.I18n 只需要两步:注入 i18n 服务,导入语言资源。

    // 添加 i18n 多语言支持
    builder.Services.AddI18nAspNetCore(defaultLanguage: "zh-CN");
    // 设置多语言来源-json
    builder.Services.AddI18nResource(option =>
    {
        var basePath = "i18n";
        option.AddJson(basePath);
    });

    接着添加 i18n 中间件,它会从用户请求中解析出对应的语言。

    var app = builder.Build();
    app.UseI18n(); // <- 放到中间件靠前的位置

    然后添加控制器或直接写中间件测试,注入 IStringLocalizer 即可。

    app.UseRouting();
    app.Use(async (HttpContext context, RequestDelegate next) =>
    {
        var localizer = context.RequestServices.GetRequiredService>();
        await context.Response.WriteAsync(localizer["购物车:商品名称"]);
        return;
    });

    启动程序,打开地址 http://localhost:5177/test?culture=en-US&ui-culture=en-US,输出就是 Product name。

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    携带请求语言信息

    Maomi.I18n 本质上是基于 ASP.NET Core 的多语言接口进行扩展的,所以它不需要自己费力解析语言,而是充分利用 ASP.NET Core 自带的 RequestLocalizationMiddleware 中间件。这个中间件会自动调用 IRequestCultureProvider 来检索请求所用的语言,我们通过 context.Features.Get() 就能拿到语言信息,大大简化了代码量。

    ASP.NET Core 默认提供了三种解析请求语言的方式:

    • URL 路由参数(QueryStringRequestCultureProvider),需要携带 culture 和 ui-culture 参数,例如 ?culture=en-US&ui-culture=en-US。
    • Cookie(CookieRequestCultureProvider),cookie 名称为 .AspNetCore.Culture,格式为 c=en-US|uic=en-US。
    • 请求头(AcceptLanguageHeaderRequestCultureProvider),也是最常用的方式,例如 Accept-Language: zh-CN,zh;q=0.9。

    开发者可以根据需要修改这三个提供器的参数名,比如改成 lan 和 ui:

    new QueryStringRequestCultureProvider()
    {
        QueryStringKey = "lan",
        UIQueryStringKey = "ui"
    }

    由于 ASP.NET Core 已经帮我们解析好了,我们只需要从 IRequestCultureFeature 中取出语言信息即可:

    var requestCultureFeature = context.Features.Get();
    var requestCulture = requestCultureFeature?.RequestCulture;

    ASP.NET Core 会按顺序依次尝试 QueryStringRequestCultureProvider、CookieRequestCultureProvider、AcceptLanguageHeaderRequestCultureProvider,一旦某个提供器成功解析出语言,就不再往后执行。如果三个都失败,才会调用自定义的提供器。你也可以调整它们的顺序或自定义实现。

    实现一个自定义 IRequestCultureProvider 也很简单,比如要求从 URL 的 c 和 uic 参数中获取语言:

    public class I18nRequestCultureProvider : IRequestCultureProvider
    {
        private readonly string _defaultLanguage;
        public I18nRequestCultureProvider(string defaultLanguage)
        {
            _defaultLanguage = defaultLanguage;
        }
        private const string RouteValueKey = "c";
        private const string UIRouteValueKey = "uic";
    
        public override Task DetermineProviderCultureResult(HttpContext httpContext)
        {
            var request = httpContext.Request;
            if (!request.RouteValues.Any())
            {
                return NullProviderCultureResult;
            }
            string? queryCulture = null;
            string? queryUICulture = null;
            if (!string.IsNullOrWhiteSpace(RouteValueKey))
            {
                queryCulture = request.RouteValues[RouteValueKey]?.ToString();
            }
            // 其他过程省略
            var providerResultCulture = new ProviderCultureResult(queryCulture, queryUICulture);
            return Task.FromResult(providerResultCulture);
        }
    }

    需要注意的是,IRequestCultureProvider 不能通过容器注入,而是在 RequestLocalizationOptions 中配置:

    services.Configure(options =>
    {
        options.RequestCultureProviders.Add(new I18nRequestCultureProvider(defaultLanguage));
    });

    想调整提供器的顺序,直接操作 options.RequestCultureProviders 集合即可。

    实现 i18n 框架

    聊完了用法,接下来我们亲手搭建一个 i18n 框架。全部代码结构如下:

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    文件说明:

    // 当前程序多语言上下文
    I18nContext.cs
    // 多语言资源接口定义
    I18nResource.cs
    // i18n 语言资源工厂
    I18nResourceFactory.cs
    // 设置当前语言作用域
    I18nScope.cs
    // 服务注入扩展
    I18nExtensions.cs
    // 从 json 文件读取多语言资源扩展
    JsonResourceExtensions.cs
    // 实现 I18nResourceFactory
    InternalI18nResourceFactory.cs
    // 自定义请求语言解析
    I18nRequestCultureProvider.cs
    // 实现 IStringLocalizer 接口
    I18nStringLocalizer.cs
    // 实现 IStringLocalizer 接口
    I18nStringLocalizer`.cs
    // 实现 I18nResource,通过 json 文件导入语言资源
    JsonResource.cs
    // 解析 json 的帮助类
    ReadJsonHelper.cs

    抽象接口

    设计多语言框架,首先要划分三个角色:使用者、框架自身、多语言提供者。使用者通过抽象接口获取 key 对应的值,提供者通过抽象接口提供键值对数据,框架本身则是桥梁。

    我们不需要关心多语言数据存在哪里——可以是 json 文件、嵌入程序集、Redis,甚至数据库。统一通过接口加载即可。

    先定义 I18nResource 接口:

    /// 
    /// i18n 语言资源.
    /// 
    /// 每个 I18nResource 对应一种语言的一个资源文件.
    public interface I18nResource
    {
        CultureInfo SupportedCulture { get; }
        LocalizedString Get(string culture, string name);
        LocalizedString Get(string culture, string name, params object[] arguments);
        IEnumerable GetAllStrings(bool includeParentCultures);
    }
    
    public interface I18nResource : I18nResource { }

    然后是多语言资源工厂,管理所有 I18nResource 实例,支持从容器中取出资源:

    public interface I18nResourceFactory
    {
        IList SupportedCultures { get; }
        IList Resources { get; }
        IList ServiceResources { get; }
        I18nResourceFactory AddServiceType(Type resourceType);
        I18nResourceFactory Add(I18nResource resource);
        I18nResourceFactory Add(I18nResource resource);
    }

    使用者接口方面,ASP.NET Core 已经定义了 IStringLocalizer 和 IStringLocalizer,我们直接实现它们即可。另外定义一个 I18nContext 来存储当前请求的语言信息,下游服务通过它获取当前语言:

    public class I18nContext
    {
        public CultureInfo Culture { get; internal set; } = CultureInfo.CurrentCulture;
    }

    接下来是实现 I18nStringLocalizer 和 I18nStringLocalizer 的核心代码。这两个类会遍历 I18nResourceFactory 中的所有资源,根据当前语言查找 key 对应的值。如果找不到,就返回 key 本身作为默认值。(具体代码较长,原文中已完整给出,此处不再重复粘贴,但确保保留在原位。)

    I18nScope 的作用是临时修改 CultureInfo.CurrentCulture,方便在控制台或测试中手动切换语言:

    public class I18nScope : IDisposable
    {
        private readonly CultureInfo _defaultCultureInfo;
        public I18nScope(string language)
        {
            _defaultCultureInfo = CultureInfo.CurrentCulture;
            CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture(language);
        }
        public void Dispose()
        {
            CultureInfo.CurrentCulture = _defaultCultureInfo;
        }
    }

    实现从 json 读取语言资源

    Maomi.I18n 自带了一个从 json 文件读取多语言的 DictionaryResource 实现。核心思路:每个项目可以创建 i18n 目录,下面再以项目名建子目录,子目录里放语言文件。

    这样做的好处是:编译时主项目的 i18n 目录会收集所有依赖项目中的语言文件,且不会冲突。打包成 nuget 时,这些文件也会被打进去,其他开发者引用 nuget 包后直接就能用。

    DictionaryResource 实现了 I18nResource,内部用一个字典存储键值对。其泛型版本 DictionaryResource 会自动与 T 类型的程序集绑定,避免跨程序集混淆。

    另外提供了扩展方法 JsonResourceExtensions,支持扫描目录、添加单个 json 文件等操作,细节都在原代码中,这里不再展开。

    管理 I18nResourceFactory 的核心实现 InternalI18nResourceFactory 也很简单,维护三个列表:支持的语言、资源实例、服务类型。

    最后通过 I18nExtensions 把服务注入容器,包括 IStringLocalizerFactory、IStringLocalizer 等。对于 ASP.NET Core 应用,还有额外的 AddI18nAspNetCore 和 UseI18n 中间件扩展。

    针对模型验证的多语言,还提供了 AddI18nDataAnnotation 扩展,让验证错误信息也能跟随语言切换。

    单元测试

    写框架不写单元测试,就像盖房子不打地基。我们用 xUnit 配合 Microsoft.AspNetCore.Mvc.Testing 来验证 i18n 框架的正确性。

    项目结构如下:

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    先构建一个测试用的 Web Host:

    using var host = await new HostBuilder()
        .ConfigureWebHost(webBuilder =>
        {
            webBuilder.UseTestServer()
                .ConfigureServices(services =>
                {
                    services.AddControllers();
                    services.AddI18n(defaultLanguage: "zh-CN");
                    services.AddI18nResource(option =>
                    {
                        var basePath = "i18n";
                        option.AddJson(basePath);
                    });
                })
                .Configure(app =>
                {
                    app.UseI18n();
                    app.UseRouting();
                    app.Use(async (HttpContext context, RequestDelegate next) =>
                    {
                        var localizer = context.RequestServices.GetRequiredService>();
                        await context.Response.WriteAsync(localizer["购物车:商品名称"]);
                        return;
                    });
                });
        })
        .StartAsync();

    通过 host.GetTestClient() 拿到 HttpClient,然后分别测试 URL 参数、Cookie、Accept-Language 头 三种方式:

    var httpClient = host.GetTestClient();
    
    // 1. 路由参数
    var response = await httpClient.GetStringAsync("/test?culture=en-US&ui-culture=en-US");
    Assert.Equal("Product name", response);
    
    response = await httpClient.GetStringAsync("/test?culture=zh-CN&ui-culture=zh-CN");
    Assert.Equal("商品名称", response);
    
    // 2. Cookie
    httpClient.DefaultRequestHeaders.Add("Cookie", ".AspNetCore.Culture=c=en-US|uic=en-US");
    response = await httpClient.GetStringAsync("/test");
    Assert.Equal("Product name", response);
    
    httpClient.DefaultRequestHeaders.Remove("Cookie");
    httpClient.DefaultRequestHeaders.Add("Cookie", ".AspNetCore.Culture=c=zh-CN|uic=zh-CN");
    response = await httpClient.GetStringAsync("/test");
    Assert.Equal("商品名称", response);
    
    // 3. Accept-Language 头
    httpClient.DefaultRequestHeaders.Remove("Cookie");
    httpClient.DefaultRequestHeaders.AcceptLanguage.Clear();
    httpClient.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("zh-CN"));
    response = await httpClient.GetStringAsync("/test");
    Assert.Equal("商品名称", response);
    
    httpClient.DefaultRequestHeaders.AcceptLanguage.Clear();
    httpClient.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US"));
    response = await httpClient.GetStringAsync("/test");
    Assert.Equal("Product name", response);

    基于 Redis 的动态多语言

    有些场景下,多语言内容需要动态更新,比如运营后台实时修改翻译,不希望重启服务。这时可以用 Redis 来存储语言数据。

    Maomi.I18n.Redis 扩展库实现了从 Redis 加载多语言数据,并利用 FreeRedis 的客户端缓存(Client-Side Caching)将数据拉到本地内存,实现“实时更新 + 高性能”的效果。

    public class RedisI18nResource : I18nResource
    {
        private readonly RedisClient _redisClient;
        private readonly string _pathPrefix;
    
        internal RedisI18nResource(RedisClient redisClient, string pathPrefix, TimeSpan expired, int capacity = 10)
        {
            _redisClient = redisClient;
            _pathPrefix = pathPrefix;
            // Redis client-side 模式
            redisClient.UseClientSideCaching(new ClientSideCachingOptions
            {
                Capacity = capacity,
                KeyFilter = key => key.StartsWith(pathPrefix),
                CheckExpired = (key, dt) => DateTime.Now.Subtract(dt) > expired
            });
            // 首次加载
            GetAllStrings(default);
        }
        // ... 实现 Get、GetAllStrings 等方法,从 Redis Hash 读取
    }

    扩展方法使用示例:

    WRedisClient cli = new RedisClient("127.0.0.1:6379,defaultDatabase=0");
    builder.Services.AddI18n(defaultLanguage: "zh-CN");
    builder.Services.AddI18nResource(option =>
    {
        option.AddRedis(cli, "language", TimeSpan.FromMinutes(100), 10);
        option.AddJson("i18n");
    });

    在 Redis 中创建一个 Hash 类型的 key(例如 language:zh-CN),往里添加键值对,客户端就能直接读到最新数据。

    nuget 打包嵌入 json

    企业内部开发时,经常把公共类库打成 nuget 包供其他团队使用。多语言资源文件也要一起打包。

    以 Demo5.Nuget 项目为例,目录结构如下:

    .NET  i18n 原理、实现一个 i18n 框架的过程详解

    修改 .csproj 文件,将 json 文件作为 Content 并设置 Pack="true",指定 PackageCopyToOutput 和 PackagePath:

    
      
        true
        Always
        contentFiles\any\any\i18n\Demo5.Nuget\en-US.json
      
      
        true
        Always
        contentFiles\any\any\i18n\Demo5.Nuget\zh-CN.json
      
    

    对于 Web 项目,编译器默认会给 .json 文件设置 属性,所以需要先关闭 EnableDefaultContentItems 避免冲突:

    false

    其他开发者引入这个 nuget 包后,项目中就会自动出现对应的语言文件。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    圆通速递查单号官网
    圆通速递查单号官网

    圆通速递查单号官网在哪里? 不少朋友都在寻找圆通快递的官方物流查询入口。别急,这就为你揭晓答案,并带你详细了解它的核心功能与服务。 圆通速递的官方网站地址是:https://www.yto.net.cn/。记住这个地址,所有官方服务都从这里开始。 官网核心功能 来到官网,你会发现它的运单查询服务是全

    在小说搜搜找不到想看的小说怎么办?小说搜搜换源功能使用详解【必学】
    在小说搜搜找不到想看的小说怎么办?小说搜搜换源功能使用详解【必学】

    小说搜搜搜索不到结果?五种实用解决方法帮你搞定 在小说搜搜里搜不到想看的书?这事儿其实挺常见。问题多半出在书源上——要么是默认的书源还没来得及收录,要么是原先的源数据失效了,更新没跟上。别急,下面这五条解决路径,基本能覆盖绝大多数情况,操作起来也不复杂。 一、通过阅读页调出换源入口手动切换 这个方法

    来看小说网最新章节免费阅读网
    来看小说网最新章节免费阅读网

    来看小说网最新章节免费阅读网-来看小说网全网小说同步更新入口 不少资深书友都在问,来看小说网的全网小说同步更新入口究竟在哪?别急,答案就在这里。接下来,就为大家详细揭开这个平台的神秘面纱,看看它究竟提供了怎样的阅读体验。 来看小说网的官方入口是:https://www.kanxiaoshuo.net

    什么是面向接口编程、面向过程编程、面向对象编程?
    什么是面向接口编程、面向过程编程、面向对象编程?

    面向接口编程通过接口解耦,降低耦合度并提升可扩展性和维护性,是面向对象思想的精髓。面向过程编程将问题拆解为步骤,用函数依次执行。面向对象编程将事物抽象为对象,封装属性和方法,通过对象协作解决问题。

    C#怎么实现过滤器Filter_C# ASP.NET Core过滤器详解教程【进阶】
    C#怎么实现过滤器Filter_C# ASP.NET Core过滤器详解教程【进阶】

    ASP.NETCore过滤器需关注执行顺序、作用域与DI生命周期。接口实现需手动注册,特性继承更常用但需满足构造函数要求。全局注册时注意scoped服务注入问题,Order值须显式设置以控制同类型过滤器执行顺序。异常过滤器仅捕获操作方法内异常,模型绑定与授权失败须分别处理。

    C#怎么实现WebAPI异常处理 C#如何在ASP.NET Core中全局捕获异常返回统一错误信息【框架】
    C#怎么实现WebAPI异常处理 C#如何在ASP.NET Core中全局捕获异常返回统一错误信息【框架】

    ASP.NETCore中UseExceptionHandler需在UseRouting之后、UseEndpoints之前注册,开发环境需移除UseDeveloperExceptionPage,仅捕获请求生命周期异常。推荐使用ProblemDetails返回标准错误,自定义异常可通过类型判断返回差异化响应。异步操作必须显式await,否则异常无法被捕获。

    C#怎么创建gRPC服务_C# ASP.NET Core gRPC通信教程【高级】
    C#怎么创建gRPC服务_C# ASP.NET Core gRPC通信教程【高级】

    gRPC开发陷阱:服务类需继承GreeterBase并返回Task,由Grpc.Tools自动生成基类。MapGrpcService须在UseRouting后注册且仅支持HTTP/2。ServerCallContext获取取消信号,超时返回DeadlineExceeded。EnableDetailedErrors可获取错误详情,但生产环境应关闭。

    C#怎么使用IHostedLifecycle_C# .NET 8生命周期钩子教程【进阶】
    C#怎么使用IHostedLifecycle_C# .NET 8生命周期钩子教程【进阶】

    IHostedLifecycleService并非控制服务启动顺序的调度器,而是单个服务的生命周期钩子。OnStartedAsync在所有StartAsync完成后才触发且无序执行,适用于状态收尾或就绪广播,但需注意超时限制。控制启动依赖应使用显式信号而非钩子。

    C#怎么实现Quartz.NET持久化 C#如何将Quartz调度任务配置持久化到数据库防止丢失【框架】
    C#怎么实现Quartz.NET持久化 C#如何将Quartz调度任务配置持久化到数据库防止丢失【框架】

    Quartz.NET任务持久化需使用AdoJobStore将调度信息存入数据库,配置需指定数据库类型、连接字符串和方言,并执行官方建表脚本。表名前缀须一致,注意数据库事务支持与集群配置,避免因类型名变更导致反序列化失败。

    C#怎么创建Identity认证_C# ASP.NET Identity用户管理教程【实战】
    C#怎么创建Identity认证_C# ASP.NET Identity用户管理教程【实战】

    先说结论:用 dotnet new mvc -au Individual 创建项目最省事,它自动生成带 Identity 的完整认证流程——注册、登录、登出、密码重置全套搞定,不用手动配 DbContext、UserManager 或写 Account 控制器。对于绝大多数场景,这是开箱即用的最佳起

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。