根本原因是oracle客户端默认以单字节编码发送请求体,而.net服务按utf-8解析,导致编码错位;须在oracle端显式设置content-type为utf-8、用utl_raw转换字节流,或改用apex_web_service传blob。
oracle 调用 .net web 服务时出现中文乱码,根本原因不是 soap 协议本身不支持中文,而是 oracle 客户端(如 utl_http、apex_web_service)发起 http 请求时,默认以 us7ascii 或系统默认单字节编码发送请求体,而 .net web api 默认按 utf-8 解析请求体 —— 两边对请求内容的编码解读错位了。
检查 .NET Web 服务接收端是否强制指定了编码
ASP.NET Core 默认用 UTF-8 解析 POST/PUT 的 body,但若手动配置了 JsonSerializerOptions.Encoder 或用了自定义 InputFormatter,可能意外禁用了 UTF-8。重点确认:
- 没在
Program.cs中调用options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;(它不改编码,但常被误认为影响解码) - 没在
Startup.ConfigureServices中注册非 UTF-8 的System.Text.Json或Newtonsoft.Json编码器 - HTTP 请求头里带了
Content-Type: application/json; charset=gb2312这类声明?如果有,.NET 会按该 charset 解 body,而 Oracle 几乎不会主动发这个头
Oracle 端必须显式设置请求体编码为 UTF-8
Oracle 自带的 UTL_HTTP 不自动设 Content-Type 字符集,也不按数据库字符集编码请求体。你必须手动写:
UTL_HTTP.set_header(l_req, 'Content-Type', 'application/json; charset=UTF-8'); UTL_HTTP.write_text(l_req, l_json_str); -- 注意:l_json_str 必须已是 UTF-8 字节流
关键点:
-
l_json_str不能是数据库里直接拼出的 VARCHAR2 字符串(它受数据库字符集限制,比如 ZHS16GBK 下存的中文,在 US7ASCII 数据库里就是乱码) - 必须用
UTL_RAW.CAST_TO_RAW+CONVERT显式转成 UTF-8 字节:UTL_HTTP.write_raw(l_req, UTL_RAW.CAST_TO_RAW(CONVERT(l_json_str, 'AL32UTF8', 'ZHS16GBK'))); - 如果数据库字符集本身就是
AL32UTF8,可省略CONVERT,但CAST_TO_RAW仍不可少 ——write_text会按数据库字符集再编码一次,导致双重编码
绕过 Oracle 编码层:改用 APEX_WEB_SERVICE(更稳)
Oracle APEX 自带的 APEX_WEB_SERVICE.MAKE_REST_REQUEST 内部已处理 UTF-8 转换逻辑,比裸用 UTL_HTTP 少踩坑。但要注意:
- 必须传
p_body_blob(BLOB),不能传p_body(CLOB)—— CLOB 仍走数据库字符集路径 - BLOB 内容需提前构造好 UTF-8 字节:
v_blob := UTL_RAW.CAST_TO_RAW(CONVERT(v_json_clob, 'AL32UTF8', 'ZHS16GBK')); - 调用时明确指定
p_content_type => 'application/json; charset=UTF-8'
调试时最容易忽略的环节
Oracle 端看不到 .NET 的原始请求体字节,所以别只查日志里打印出来的字符串 —— 那已经是经过 Oracle 字符集转换后的“假象”。真正要抓的是:
- 用 Wireshark 或 Fiddler 在 .NET 服务所在机器上抓包,看 HTTP body 的十六进制是否以
EF BB BF(UTF-8 BOM)开头,或中文是否为 3 字节序列(如“中”是E4 B8 AD) - 在 .NET 侧加中间件,把
HttpContext.Request.Body全量读成 byte[] 并记录 hex dump,和抓包比对是否一致 - 如果 Oracle 和 .NET 之间还经过 Nginx / IIS / OHS,它们也可能重写
Content-Type或做编码转换,得逐层排查











