.NET Maui Android中POST请求因URL含下划线抛出Java异常
.NET Maui Android 请求含下划线域名异常的解决方案
问题根源
Android平台的TLS SNI扩展在处理域名时,严格遵循STD3 ASCII规则,不允许域名包含下划线。当请求https://changed_for_privacy.com这类域名时,SNI握手阶段的IDNA解析会抛出Java.Lang.IllegalArgumentException异常,而Postman等工具因使用宽松解析策略可正常请求。
解决方案
方法一:自定义OkHttp客户端(推荐)
.NET Maui在Android底层依赖OkHttp,可通过配置OkHttp允许含下划线的域名:
- 给Android项目安装
Square.OkHttp3NuGet包 - 创建自定义HttpMessageHandler:
using OkHttp3; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; namespace YourProject.Platforms.Android { public class CustomOkHttpHandler : HttpMessageHandler { private readonly OkHttpClient _okHttpClient; public CustomOkHttpHandler() { var clientBuilder = new OkHttpClient.Builder() .Dns(Dns.Safe) // 使用宽松的DNS解析策略 .Build(); _okHttpClient = clientBuilder; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var okRequestBuilder = new Request.Builder().Url(request.RequestUri.ToString()); // 复制请求方法和内容 if (request.Content != null) { var contentBytes = await request.Content.ReadAsByteArrayAsync(cancellationToken); var mediaType = MediaType.Parse(request.Content.Headers.ContentType?.ToString()); var okRequestBody = RequestBody.Create(contentBytes, mediaType); okRequestBuilder.Method(request.Method.Method, okRequestBody); } else { okRequestBuilder.Method(request.Method.Method, null); } // 复制请求头 foreach (var header in request.Headers) { okRequestBuilder.AddHeader(header.Key, string.Join(", ", header.Value)); } var okResponse = await _okHttpClient.NewCall(okRequestBuilder.Build()).ExecuteAsync(cancellationToken); // 转换为.NET HttpResponseMessage var response = new HttpResponseMessage((System.Net.HttpStatusCode)(int)okResponse.Code) { Content = new ByteArrayContent(await okResponse.Body.BytesAsync()) }; // 复制响应头 foreach (var header in okResponse.Headers) { response.Headers.TryAddWithoutValidation(header.Key, string.Join(", ", header.Value)); } return response; } } }
- 修改HttpClient初始化逻辑,针对Android平台使用自定义Handler:
internal class MyService { private readonly HttpClient httpClient; public MyService() { #if ANDROID httpClient = new HttpClient(new YourProject.Platforms.Android.CustomOkHttpHandler()); #else httpClient = new HttpClient(); #endif } // 其余代码保持不变 }
方法二:禁用SNI(不推荐,有安全风险)
通过系统属性禁用SNI扩展,跳过域名格式检查,但会降低TLS连接的安全性:
在Android项目的MainApplication.cs中添加如下代码:
using Android.App; using Android.Runtime; namespace YourProject.Platforms.Android { [Application] public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); public override void OnCreate() { base.OnCreate(); // 禁用SNI扩展 System.Properties.SetProperty("jsse.enableSNIExtension", "false"); } } }
方法三:自定义主机名验证器
跳过特定含下划线域名的SNI验证:
- 创建自定义主机名验证器:
using Android.Net.Http; using Java.Security.Cert; namespace YourProject.Platforms.Android { public class CustomHostnameVerifier : Java.Lang.Object, IHostnameVerifier { public bool Verify(string hostname, ISSLSession session) { // 允许目标域名,或保留默认验证逻辑 return hostname.Equals("changed_for_privacy.com") || HttpsURLConnection.DefaultHostnameVerifier.Verify(hostname, session); } } }
- 修改HttpClient初始化:
internal class MyService { private readonly HttpClient httpClient; public MyService() { #if ANDROID var handler = new AndroidMessageHandler(); handler.HostnameVerifier = new YourProject.Platforms.Android.CustomHostnameVerifier(); httpClient = new HttpClient(handler); #else httpClient = new HttpClient(); #endif } // 其余代码保持不变 }
内容的提问来源于stack exchange,提问作者Bob
相关产品推荐
相关产品推荐

