You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

负载均衡Elastic Beanstalk下ASP.NET Core HTTPS请求挂起问题排查

问题描述

将部署在Linux上的ASP.NET Core后端从单实例Elastic Beanstalk环境转为负载均衡模式,并配置了AWS证书管理器(ACM)颁发的HTTPS证书,但前端请求后端时一直处于挂起状态,超时后失败;切换回单实例模式则正常。

当前前端通过HTTP访问http://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002可正常连接,但以HTTPS访问时出现混合内容拦截问题,因此需要将后端切换为HTTPS访问(如https://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002或自定义域名https://vepo-qa.com)。

已在Route 53配置vepo-qa.com域名记录,添加了HTTPS监听器,但存在以下疑问:

  • 后端代码中webBuilder.UseUrls("http://*:5002")是否需要修改,是否要匹配监听器端口?
  • 仅为监听器分配证书是否足够,是否需要在后端添加私钥或配置.ebextensions文件?
  • 前端请求地址应改为HTTPS的EB地址还是自定义域名,是否需要告知Elastic Beanstalk自定义域名?

另外还有两个问题:

  • 当后端为HTTPS时,本地HTTP前端是否无法访问?
  • 目前从Firebase HTTPS前端访问https://vepo-qa.com时出现CORS错误。

Program.cs

namespace Vepo.Web
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.UseUrls("http://*:5002");
                });
    }
}

Startup.cs

namespace Vepo.Web
{
    public class Startup
    {

        private class CustomTransformer : HttpTransformer
        {
            public override async ValueTask TransformRequestAsync(HttpContext httpContext,
                HttpRequestMessage proxyRequest, string destinationPrefix, CancellationToken cancellationToken)
            {
                await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);
                var queryContext = new QueryTransformContext(httpContext.Request);
                proxyRequest.RequestUri = RequestUtilities.MakeDestinationAddress("https://maps.googleapis.com/maps/api", httpContext.Request.Path, queryContext.QueryString);
                proxyRequest.Headers.Host = null;
            }
        }

        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;
            Env = env;
        }

        public IConfiguration Configuration { get; }
        public IWebHostEnvironment Env { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<VepoContext>(opt =>
                opt.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), o => o.UseNetTopologySuite())
                   .EnableSensitiveDataLogging().EnableDetailedErrors().LogTo(Console.WriteLine));
            services.AddHttpContextAccessor();
            services.AddHttpForwarder();
            services.AddTransient<UserResolverService>();
            services.AddTransient<ExtendedVepoContext>();
            services.AddControllers().AddJsonOptions(opt =>
            {
                opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
                opt.JsonSerializerOptions.Converters.Add(new GeoJsonConverterFactory());
            });
            services.AddControllers(config =>
            {
                var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
                config.Filters.Add(new AuthorizeFilter(policy));
            }).AddNewtonsoftJson(x =>
            {
                x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                x.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
            }).AddJsonOptions(opt =>
            {
                opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
            });

            if (Env.IsDevelopment())
            {
                FirebaseApp.Create(new AppOptions()
                {
                    Credential = GoogleCredential.FromFile("firebase_admin_sdk_development.json"),
                }, "vepo-dev-e5b8a");
            }
            if (Env.IsQa())
            {
                FirebaseApp.Create(new AppOptions()
                {
                    Credential = GoogleCredential.FromFile("firebase_admin_sdk_qa.json"),
                }, "vepo-qa");
            }

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                   .AddJwtBearer(options =>
                   {
                       options.Authority = Configuration["FirebaseAuthority"];
                       options.TokenValidationParameters = new TokenValidationParameters
                       {
                           ValidateIssuer = true,
                           ValidIssuer = Configuration["FirebaseAuthority"],
                           ValidateAudience = true,
                           ValidAudience = Configuration["FirebaseProjectId"],
                           ValidateLifetime = true
                       };
                   });
            services.AddMvc().AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
            });

            // 仓储与服务依赖注入配置
            services.AddScoped(typeof(IGroceryItmEstsRepository), typeof(GroceryItmEstsRepository));
            services.AddScoped(typeof(IAllVgnItmEstsRepository), typeof(AllVgnItmEstsRepository));
            services.AddScoped(typeof(IEventItmEstsRepository), typeof(EventItmEstsRepository));
            services.AddScoped(typeof(IFashionItmEstsRepository), typeof(FashionItmEstsRepository));
            services.AddScoped(typeof(IRecipeItmEstsRepository), typeof(RecipeItmEstsRepository));
            services.AddScoped(typeof(IGroceryStoreItmEstsRepository), typeof(GroceryStoreItmEstsRepository));
            services.AddScoped(typeof(IRestaurantItmEstsRepository), typeof(RestaurantItmEstsRepository));
            services.AddScoped(typeof(IUsersRepository), typeof(UsersRepository));
            services.AddScoped(typeof(IBookMarksRepository), typeof(BookMarksRepository));
            services.AddScoped(typeof(IGroceryItmsRepository), typeof(GroceryItmsRepository));
            services.AddScoped(typeof(IEventItmsRepository), typeof(EventItmsRepository));
            services.AddScoped(typeof(IFashionItmsRepository), typeof(FashionItmsRepository));
            services.AddScoped(typeof(IRecipeItmsRepository), typeof(RecipeItmsRepository));
            services.AddScoped(typeof(IGroceryStoreItmsRepository), typeof(GroceryStoreItmsRepository));
            services.AddScoped(typeof(IRestaurantItmsRepository), typeof(RestaurantItmsRepository));
            services.AddScoped(typeof(IMenuItmsRepository), typeof(MenuItmsRepository));
            services.AddScoped<IUsersService, UsersService>();
            services.AddScoped<IBookMarksService, BookMarksService>();
            services.AddScoped<IMenuItmEstsService, MenuItmEstsService>();
            services.AddScoped<IGroceryStoreItmEstsService, GroceryStoreItmEstsService>();
            services.AddScoped<IRestaurantItmEstsService, RestaurantItmsService>();
            services.AddScoped<IEventItmEstsService, EventItmEstsService>();
            services.AddScoped<IFashionItmEstsService, FashionItmEstsService>();
            services.AddScoped<IRecipeItmEstsService, RecipeItmEstsService>();
            services.AddScoped<IGroceryItmEstsService, GroceryItmEstsService>();
            services.AddScoped<IAllVgnItmEstsService, AllVgnItmEstsService>();
            services.AddScoped<IMenuItmsService, MenuItmsService>();
            services.AddScoped<IGroceryStoreItmsService, GroceryStoreItmsService>();
            services.AddScoped<IRestaurantItmsService, RestaurantItmsService>();
            services.AddScoped<IEventItmsService, EventItmsService>();
            services.AddScoped<IFashionItmsService, FashionItmsService>();
            services.AddScoped<IRecipeItmsService, RecipeItmsService>();
            services.AddScoped<IGroceryItmsService, GroceryItmsService>();
            services.AddScoped(typeof(IMenuItmEstsRepository), typeof(MenuItmEstsRepository));

            services.AddScoped<ISearchService, SearchService>(serviceProvider =>
                new SearchService(
                    Configuration["openSearchEndpoint"],
                    Configuration["openSearchUsername"],
                    Configuration["openSearchPassword"],
                    Configuration["openSearchIndexName"]));

            services.AddScoped<IGroceryItmsSearchIndexService, GroceryItmsSearchIndexService>();
            services.AddScoped<IRecipeItmsSearchIndexService, RecipeItmsSearchIndexService>();
            services.AddScoped<IEventItmsSearchIndexService, EventItmsSearchIndexService>();
            services.AddScoped<IFashionItmsSearchIndexService, FashionItmsSearchIndexService>();
            services.AddScoped<IMenuItmsSearchIndexService, MenuItmsSearchIndexService>();
            services.AddScoped<IRestaurantItmsSearchIndexService, RestaurantItmsSearchIndexService>();
            services.AddScoped<IGroceryStoreItmsSearchIndexService, GroceryStoreItmsSearchIndexService>();
            services.AddScoped<IAllVgnItmsSearchIndexService, AllVgnItmsSearchIndexService>();

            services.AddAutoMapper(
                typeof(EstProfile), typeof(VgnItmEstProfile), typeof(VgnItmProfile),
                typeof(EventItmEstProfile), typeof(EventItmProfile), typeof(FashionItmEstProfile),
                typeof(FashionItmProfile), typeof(RecipeItmEstProfile), typeof(RecipeItmProfile),
                typeof(GroceryItmEstProfile), typeof(GroceryItmProfile), typeof(GroceryStoreItmEstProfile),
                typeof(GroceryStoreItmProfile), typeof(RestaurantItmEstProfile), typeof(RestaurantItmProfile),
                typeof(MenuItmEstProfile), typeof(MenuItmProfile));
            services.AddSwaggerGen();
        }

        [Obsolete]
        public void Configure(
            IApplicationBuilder app,
            IWebHostEnvironment env,
            VepoContext context,
            IGroceryItmsSearchIndexService searchIndexService,
            IHttpForwarder forwarder)
        {
            if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
            NpgsqlConnection.GlobalTypeMapper.UseNetTopologySuite();
            app.UseAuthentication();
            app.UseAuthorization();

            var httpClient = new HttpMessageInvoker(new SocketsHttpHandler()
            {
                UseProxy = false,
                AllowAutoRedirect = false,
                AutomaticDecompression = DecompressionMethods.None,
                UseCookies = false,
                ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
                ConnectTimeout = TimeSpan.FromSeconds(15),
            });
            var transformer = new CustomTransformer();
            var requestConfig = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(100) };

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                RequestDelegate googleMapsApi = async httpContext =>
                {
                    var error = await forwarder.SendAsync(httpContext, "https://maps.googleapis.com/maps/api/", httpClient, requestConfig, transformer);
                    if (error != ForwarderError.None)
                    {
                        var errorFeature = httpContext.GetForwarderErrorFeature();
                        var exception = errorFeature.Exception;
                    }
                };
                endpoints.Map("/place/{**catch-all}", googleMapsApi);
                endpoints.Map("/geocode/{**catch-all}", googleMapsApi);
            });

            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Test1 Api v1"));
            searchIndexService.ReIndex(context, SearchIndexes.All);
        }
    }

    public static class HostEnvironmentExtensions
    {
        public static bool IsQa(this IHostEnvironment hostingEnvironment)
        {
            return hostingEnvironment.IsEnvironment("Qa");
        }
    }
}

相关.ebextensions配置文件

securelistener-alb.config

option_settings:
  aws:elbv2:listener:443:
    ListenerEnabled: 'true'

https-instance-securitygroup.config

Resources:
  sslSecurityGroupIngress: 
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}
      IpProtocol: tcp
      ToPort: 443
      FromPort: 443
      CidrIp: 0.0.0.0/0

sg-ingressfromlb.config

Resources:
  sslSecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}
      IpProtocol: tcp
      ToPort: 1000
      FromPort: 1000
      SourceSecurityGroupName: {"Fn::GetAtt" : ["AWSEBLoadBalancer" , "SourceSecurityGroup.GroupName"]}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 20:10:52