22

ASP.NET Core JSON 中文編碼問題與序列化參數設定

 2 years ago
source link: https://blog.darkthread.net/blog/aspnet-core-json-setting/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
ASP.NET Core JSON 中文編碼問題與序列化參數設定-黑暗執行緒

轉進 ASP.NET Core 世界,依循過去寫 MVC 經驗,加上參考網路技術文章(當然,還有流著奶與蜜的 Stackoverflow) 大致還算都順利,但不時發現新的眉角。

今天遇到的問題是 Razor Page 傳回 JsonResult 時,中文字元被轉成 UCN (Unicode Character Name,例如:"\u9ED1\u6697\u57F7\u884C\u7DD2") 編碼。Razor Page 程式範例如下,OnGetJsonData() 時使用 new JsonResult() 回傳簡單資料物件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using PTSWeb.Models.Data;

namespace PTSWeb.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;

        public IndexModel(ILogger<IndexModel> logger,)
        {
            _logger = logger;
        }

        public void OnGet()
        {

        }

        public ActionResult OnGetJsonData()
        {
            return new JsonResult(new
            {
                Blog = "darkthread",
                Name = "黑暗執行緒"
            });
        }
    }
}

如下圖所示,在預設情況下,中文字串將會被轉換成 UCN,另外屬性名稱的第一個字元被轉成小寫(我慣用大寫,程式碼產生器處理時可保持前後端一致):

這類問題對對我不算陌生,之前在 cshtml 就遇過:ASP.NET Core View 中文變 & # x4E2D; & # x6587;,大約知道與 Encoder 設定有關,差別在上回是 HtmlEncoder,ASP.NET Core 3.0 起預設改用 System.Text.Json 處理 JSON 序列化與反序列化,找到方法調整 System.Text.Json 的 Encoder 設定即可解決。

查到做法是在 Startup.ConfigureServices() 的 AddRazorPages()、AddControllers()、AddMvc() 後接上 AddJsonOptions() 修改預設設定:(我一併取消屬性名稱強制轉頭文字小寫(Camel Case)的功能)

public void ConfigureServices(IServiceCollection services)
{
    //... 略 ....
    services.AddRazorPages()
        .AddJsonOptions(options =>
        {
            //原本是 JsonNamingPolicy.CamelCase,強制頭文字轉小寫,我偏好維持原樣,設為null
            options.JsonSerializerOptions.PropertyNamingPolicy = null;
            //允許基本拉丁英文及中日韓文字維持原字元
            options.JsonSerializerOptions.Encoder = 
                JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs);
        });
}

不過,很有預感未來很有可能因為 System.Text.Json 的某個限制讓我想換回 Json.NET,所以提前部署,預圥查了切回去使用 Json.NET 的方法:

先用 NuGet 安裝 Microsoft.AspNetCore.Mvc.NewtonsoftJson:

AddRazorPages()、AddControllers()、AddMvc() 後方接 AddNewtonsoftJson():(Json.NET 在 ASP.NET Core 預設也會將屬性名稱轉成頭文字小寫,要保持原樣需呼叫 UseMemberCasing() )

services.AddRazorPages()
    .AddNewtonsoftJson(options =>
    {
        options.UseMemberCasing();
    });
and has 7 comments

Comments

# 2022-02-18 04:25 PM by Eason

請問一下 我用.net 6 開了一個 webapi 專案 在Promgram.cs 的service 跟您一樣 都在AddRazorPages()、AddControllers()、AddMvc() 接上 AddJsonOptions() 但還是無法將json 轉成,請問是哪裡有漏了嗎 程式如下

Service 已加入

builder.Services.AddControllers() .ConfigureApiBehaviorOptions(options => { options.SuppressModelStateInvalidFilter = true; options.SuppressMapClientErrors = true; }).AddJsonOptions(options => { options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs); options.JsonSerializerOptions.WriteIndented = true; }); builder.Services.AddMvcCore().AddJsonOptions(options => { options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs); options.JsonSerializerOptions.WriteIndented = true; }); builder.Services.AddMvc().AddJsonOptions(options => { options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs); options.JsonSerializerOptions.WriteIndented = true; }); builder.Services.AddRazorPages().AddJsonOptions(options => { options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs); options.JsonSerializerOptions.WriteIndented = true; });

json程式部分

var result = new { ErrorMessage = "你好!", ErrorMessage2 = "YO" }; var js = JsonSerializer.Serialize(result);

結果 {"ErrorMessage":"\u4F60\u597D!","ErrorMessage2":"YO"}

# 2022-02-19 12:31 AM by Eason

謝謝 Jeffrey 提醒 我寫了一個我目前遇到問題的專案在github 位置在 https://github.com/HsunsProjects/Net6WebAPI 專案裡面有兩隻api 分別是api/Question/Why (這支是我目前有問題的API,不知道為什麼在program.cs 裡的 service .AddJsonOptions 無法編碼中文) 和api/Question/Solved(這知是我知道怎麼處理的方法,但想像版主一樣寫在service裡處理) 想請問有沒有大大可以幫忙解答一下

# 2022-02-19 10:25 AM by Jeffrey

to Eason, 我修改了一個版本,修改對照在這裡 https://github.com/darkthread/Net6WebAPI/commit/4d61a88fe1f7113e3987b7fbfb30d4b146132e6a 發現幾個問題: 1. 只需 AddMvc() 就好,它涵蓋 AddControllers()、AddMvcCore()... 參考:https://blog.darkthread.net/blog/add-mvc-to-razor-project/ 2. 記得 dotnet new gitignore 加上 .gitignore,bin、obj 一般是不需要放進版控的 3. .AddJsonOptions() 影響的是 Controller.Json() 的序列化行為,如果要自己呼叫 JsonSerializer.Serialize() 要另外設定,建議做法是 QuestionController 改繼承 Controller,回傳結果改成 return this.Json(result)。

# 2022-02-19 04:26 PM by Eason

To Jeffrey 大大第1、2點的提醒 第一點主要是要測試到底加在哪會有差所以通通給他加下去了啦,哈哈 :) 然後也謝謝第3點解除了我心中的問題,原來AddJsonOptions()只影響了Controller.Json() 只是因為我開啟的是WEBAPI專案,他預設是繼承ControllerBase,我以為是一樣的意思 雖然工作都是C# MVC開發,但對於很多很基礎的問題仍然不太了解為什麼,也因此常常來拜訪黑暗執行緒 真的很感謝 Jeffrey 和這個平台 :)

Post a comment

Comment
Name Captcha 55 - 34 =

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK