.NET Core 3.1 IHostBuilder Apache 托管与 Kestrel Program.cs 设置

.NET Core 3.1 IHostBuilder Apache Hosting With Kestrel Program.cs Setup

我在 CentOS 7 上有一个 Web 应用程序 运行,Apache 服务器用作 Kestrel 的代理。在我的 Program.cs 文件中,我使用的是以下内容,它似乎工作正常:

    public static void Main(string[] args)
    {
        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .Build();

        var host = new WebHostBuilder()
            .UseKestrel()
            .UseConfiguration(config)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .UseUrls("http://localhost:5555")
            .Build();

        host.Run();
    }

但看起来 .NET Core 的东西最近更新了,我正在尝试以与下面相同的方式使用 IHostBuilder,这似乎在本地工作正常,但在生产服务器上我收到错误“代理错误 502:代理服务器从上游服务器收到无效响应”。

    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)            
            .ConfigureWebHostDefaults(webBuilder =>
            {                    
                webBuilder.UseKestrel();
                webBuilder.UseContentRoot(Directory.GetCurrentDirectory());
                webBuilder.UseConfiguration(new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddCommandLine(args).Build());
                webBuilder.UseUrls("https://localhost:5555");
                webBuilder.UseStartup<Startup>();
            });

我不确定这是否与我服务器上的域配置有任何关系 - 使用上面的第一个示例再次正常工作 - 或者我是否需要在 Program.cs 或 Startup.cs 将使用 IHostBuilder 解决此问题。

与此同时,我将继续使用我拥有的东西,因为它工作得很好。

提前感谢任何可以帮助阐明这一点的人。如果有任何其他信息可以提供帮助,请告诉我。

可能是本地主机绑定或 SSL 安全导致了问题。
确保您在项目中使用的 HTTPS 证书可以被代理信任,或者禁用在 apache 中的检查。

从 .Net 的角度来看,尝试将以下内容添加到您的项目中

public class ConfigHelper
{
    /// <summary>
    /// This method is used in the service startup to read hosting configuration options from the applications settings
    /// The following section can be included in your appsettings.json file to specify the binding, ports and certificate information.
    /// The certificate should be self-signed in .pfx format
    /// 
    ///   "Hosting": {
    ///       "HTTPBinding": "DISABLED",
    ///       "HTTPPort": 0,
    ///       "HTTPSBinding": "ANY",
    ///       "HTTPSPort": 5555,
    ///       "SSLCert": "PathToCert"
    ///       }
    /// </summary>
    /// <param name="configuration">The configuration option loaded from a local config file</param>
    /// <param name="options">The KestrelServerOptions from WebHost.CreateDefaultBuilder(args).UseKestrel(options => ConfigHelper.SetupHostingOptions(configuration, options);)</param>
    public static void SetupHostingOptions(IConfigurationRoot configuration, KestrelServerOptions options)
    {
        IPAddress httpBinding = ConfigHelper.GetAddressFromSetting(configuration.GetValue<string>("Hosting:HTTPBinding"));
        IPAddress httpsBinding = ConfigHelper.GetAddressFromSetting(configuration.GetValue<string>("Hosting:HTTPSBinding"));
        if (httpBinding != null && httpBinding != IPAddress.None)
        {
            if (configuration.GetValue<int?>("Hosting:HTTPPort") != null)
                options.Listen(httpBinding, configuration.GetValue<int>("Hosting:HTTPPort"));
        }
        if (httpsBinding != null && httpsBinding != IPAddress.None)
        {
            int httpsPort = configuration.GetValue<int?>("Hosting:HTTPSPort") ?? 443;
            string sslCert = configuration.GetValue<string>("Hosting:SSLCert");
            string sslCertPassword = configuration.GetValue<string>("Hosting:SSLCertPassword");
            if (sslCert != null)
            {
                if (sslCertPassword != null)
                    options.Listen(httpsBinding, httpsPort, listenOptions => { listenOptions.UseHttps(sslCert, sslCertPassword); });
                else
                    options.Listen(httpsBinding, httpsPort, listenOptions => { listenOptions.UseHttps(sslCert); });
            }
        }
    }
    /// <summary>
    /// Gets the IP address from the configuration setting
    /// </summary>
    /// <param name="httpBinding"></param>
    /// <returns></returns>
    public static IPAddress GetAddressFromSetting(string httpBinding)
    {
        if (string.IsNullOrWhiteSpace(httpBinding) || httpBinding.ToUpper() == "DISABLED")
            return null;
        else
            httpBinding = httpBinding.ToUpper();
        IPAddress bindingIp = null;
        switch (httpBinding.ToUpper())
        {
            case "DISABLED":
                bindingIp = null;
                break;
            case "ANY":
                bindingIp = IPAddress.Any;
                break;
            case "IPV6ANY":
                bindingIp = IPAddress.IPv6Any;
                break;
            case "IPV6LOOPBACK":
                bindingIp = IPAddress.IPv6Loopback;
                break;
            case "IPV6NONE":
                bindingIp = IPAddress.IPv6None;
                break;
            case "LOOPBACK":
                bindingIp = IPAddress.Loopback;
                break;
            case "NONE":
                bindingIp = IPAddress.None;
                break;
            default:
                bool ipParsed = false;
                try
                {
                    bindingIp = IPAddress.Parse(httpBinding);
                    ipParsed = true;
                }
                catch(System.Exception)
                {

                }
                if(!ipParsed)
                {
                    IPHostEntry hostEntry = Dns.GetHostEntry(httpBinding);
                    if (hostEntry.AddressList.Length > 0)
                    {
                        bindingIp = hostEntry.AddressList[0];
                        ipParsed = true;
                    }
                }
                if (!ipParsed)
                {
                    throw new System.Exception("Failed to parse IP address from '" + httpBinding + "'");
                }
                break;
        }
        return bindingIp;
    }
}

然后在你的 main

    public static void Main(string[] args)
    {
        // Start the application
        CreateHostBuilder(args).Build().Run();
    }
    /// <summary>
    /// Create the service objects
    /// </summary>
    /// <param name="args">Command line arguments</param>
    /// <returns>The configured host builder object</returns>
    public static IHostBuilder CreateHostBuilder(string[] args)
    {
        // Load the configuration object
        var configuration = new ConfigurationBuilder()
            .AddCommandLine(args)
            .SetBasePath(Environment.CurrentDirectory)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true)
            .AddEnvironmentVariables()
            .Build();
        // Create the host builder object
        return Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder
                    .UseStartup<Startup>()
                    .UseConfiguration(configuration)
                    .UseKestrel(options => { ConfigHelper.SetupHostingOptions(configuration, options); });
            });
    }

那么你appsettings.json应该有一个像

这样的部分
 "Hosting": {
    "HTTPBinding": "DISABLED",
    "HTTPPort": 0,
    "HTTPSBinding": "ANY",
    "HTTPSPort": 5555,
    "SSLCert": "sslcert.pfx",
    "SSLCertPassword": "sslcertpasswordhere"
  }

如果您在同一个 centos 实例上从 apache 代理到 kestrel 服务,那么您可以考虑 运行 外部世界在 HTTPS 中的 apache 并在两者之间使用 http(或者只是跳过使用一起代理)

哇...这是因为服务 运行 我的网络应用程序在 http 上 运行 但强制重定向到 https。问题出在这一行:

webBuilder.UseUrls("https://localhost:5555");

需要这样:

webBuilder.UseUrls("http://localhost:5555");

我还测试了 Xavier 的解决方案,它也有效。所以感谢您提供解决方案。