You need to enable JavaScript to run this app. .NET Core 3.1部署IIS 8.5后WebRequest超时,调试/Postman运行正常
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 3.1部署IIS 8.5后WebRequest超时,调试/Postman运行正常

问题场景
  • 部署基础环境:运行于Windows Server 2012 R2、IIS 8.5上的.NET Core 3.1应用
  • 核心异常表现:应用调用WebRequest推送FCM通知时出现请求超时
  • 反常的复现特征:完全相同的请求逻辑,在Postman测试、服务器本机Visual Studio调试模式下均可正常运行,仅当应用通过IIS托管发布后才会触发超时故障。
  • 完整业务链路:
    • 用户在前端填写通知表单
    • 用户提交表单,向Web应用发起POST请求
    • 应用读取用户提交的表单数据,构造独立的WebRequest向fcm.googleapis.com发起POST调用,向对应安卓设备推送通知

排查说明:所有测试操作均直接在目标服务器上完成,没有通过外部客户端跨网验证。为了定位问题甚至专门在服务器上安装了Visual Studio,调试阶段全程没有复现异常,仅IIS托管运行时会触发故障。

关联业务代码

面向用户的表单提交接口

[HttpPost]
public async Task<IActionResult> Create(CreateViewModel vm)
{
    var request = new CreateRequest()
    {
        Author = String.IsNullOrEmpty(User.Identity.Name) ? "Guest" : User.Identity.Name,
        Description = vm.Description, 
    };
    var response = await _notificationService.Create(request);
    vm.IsValid = response.IsSuccessfull;
    vm.ShowBaseMessage = true;
    vm.BaseMessage = response.Message;
    return View("_CreateModalPartial", vm);
}

上述接口接收到的参数,会直接传递给负责通知提交的业务服务模块。

发起FCM通知请求的核心实现

try
{
    var tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send");
    tRequest.Method = "post";
    tRequest.Headers.Add(string.Format("Authorization: key={0}", "apiKey"));
    tRequest.Headers.Add(string.Format("Sender: id={0}", "senderId"));
    tRequest.ContentType = "application/json";
    var payload = new
    {
        to = clientToken,
        data = new
        {
            author = author,
            description = description
        }
    };

    string postbody = JsonConvert.SerializeObject(payload).ToString();
    Byte[] byteArray = Encoding.UTF8.GetBytes(postbody);
    tRequest.ContentLength = byteArray.Length;
    using (Stream dataStream = await tRequest.GetRequestStreamAsync())
    {
        dataStream.Write(byteArray, 0, byteArray.Length);
        using (WebResponse tResponse = tRequest.GetResponse())
        {
            using (Stream dataStreamResponse = tResponse.GetResponse())
            {
                if (dataStreamResponse != null)
                    using (StreamReader tReader = new StreamReader(dataStreamResponse))
                    {
                        String sResponseFromServer = tReader.ReadToEnd();
                    }
            }
        }
    }
}
catch(Exception e)
{
    _logger.LogWarning(e.Message);
}

故障排查与修复方向

  • 校验IIS应用池身份的网络出站权限
    调试模式下应用以当前登录服务器的用户身份运行,通常具备完整的外网访问权限;但IIS应用池默认使用的ApplicationPoolIdentity虚拟账号,若没有被加入出站防火墙、内网强制代理的允许列表中,就会直接出现外网请求超时。需要先在IIS应用池高级设置中确认托管进程的运行身份,将该账号加入出站防火墙允许规则,若服务器配置了强制代理,需为该账号配置对应的代理授权或绕过规则。
  • 修复TLS版本兼容问题
    .NET Core 3.1默认的WebRequest底层在Windows Server 2012 R2环境下,可能默认未启用TLS 1.2协议,而fcm.googleapis.com早已强制要求TLS 1.2及以上版本才能正常访问。调试模式下应用会自动检测系统支持的TLS版本,可正常完成握手;但IIS托管的进程若未显式指定TLS版本,就会因为TLS握手无响应触发超时。可在程序启动的最开头补充全局配置,强制启用TLS 1.2:
    ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
    
  • 修复异步上下文混用导致的线程饥饿
    现有代码存在异步逻辑混用同步阻塞调用的问题:已经通过await获取了请求流,后续却直接调用同步的GetResponse()方法,在ASP.NET Core的请求上下文里,这种写法很容易触发线程池饥饿,最终表现为请求超时。需要将这部分替换为全异步的调用写法:
    // 替换原有同步的响应获取逻辑
    using WebResponse tResponse = await tRequest.GetResponseAsync();
    await using Stream dataStreamResponse = await tResponse.GetResponseAsync();
    
  • 适配服务器代理配置
    调试模式下应用会自动读取当前登录用户的IE代理配置,而IIS托管的进程默认不会加载用户级别的代理配置,如果服务器必须通过代理才能访问外网,就会因为未走代理直连被网络设备拦截,最终触发超时。可以在WebRequest对象上显式指定与系统一致的代理配置,或在项目配置文件中补充全局代理参数。

内容的提问来源于stack exchange,提问作者MatR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.07 13:24:03