aspnet / SignalR

[Archived] Incredibly simple real-time web for ASP.NET Core. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
2.38k stars 446 forks source link

Cross domain problem #2771

Closed duuliy closed 6 years ago

duuliy commented 6 years ago

The 1.0.2 version has no System.Web.Cors. HTTP's cros is also not cross domain....webpack's proxy also can not. Now I want to cross the domain. What do you do?

davidfowl commented 6 years ago

https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1

duuliy commented 6 years ago

It is configured through this address。。。。The configuration is as follows: @davidfowl

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Reflection;
using System.Globalization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.FileProviders;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Diagnostics;
using DataTables.AspNet.AspNetCore;
using Newtonsoft.Json;
using Eson.Core.Web.Services;
using Eson.Core.Dal.Model;
using Eson.Core.Web.Hubs;
using Eson.Core.Web.Data;
using Eson.SignalR.ProgressManager;
using Eson.SignalR.SocialCircle;
using Eson.Core.Web.Settings;
using Eson.Core.Web.Helpers;
#if DEBUG
using Swashbuckle.AspNetCore.Swagger;
# endif

namespace Eson.Core.Web
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            var configHelper = new ConnectionNameConfigHelper(Configuration);
            // 获得连接名
            string connectionName = configHelper.GetConnectionName();
            // 获得连接字符串
            string connectionString = Configuration.GetConnectionString(connectionName);
            string assemblyFullName = this.GetType().Assembly.FullName;
            services.AddDbContextPool<ApplicationDbContext>(options => options.UseSqlServer(connectionString, b => b.MigrationsAssembly(assemblyFullName)));
            services.AddUnitOfWork<ApplicationDbContext>(ServiceLifetime.Scoped);

            services.AddIdentity<SystemUser, SystemRole>((opt) =>
            {
                opt.Password.RequireNonAlphanumeric = false;
                opt.Password.RequireUppercase = false;

                opt.SignIn.RequireConfirmedEmail = false;
                opt.SignIn.RequireConfirmedPhoneNumber = false;

                // 设置为空,标识不验证用户名
                opt.User.AllowedUserNameCharacters = null;
            })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddUserStore<SystemUserStore>()
            .AddUserManager<SystemUserManager>()
            .AddDefaultTokenProviders();

            string loginPath = "/Account/Login/";
            if (Directory.Exists(DirectoryHelper.ClientUIDir))
            {
                loginPath = M.FacadePath;
            }
            services.ConfigureApplicationCookie((opt) =>
            {
                opt.Events.OnRedirectToLogin = (context) =>
                {
                    if (context.Request.Path.StartsWithSegments("/api") && context.Response.StatusCode == (int)HttpStatusCode.OK)
                    {
                        context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    }
                    else
                    {
                        context.Response.Redirect(context.RedirectUri);
                    }
                    return Task.CompletedTask;
                };
                opt.LoginPath = new PathString(loginPath);
                opt.AccessDeniedPath = new PathString("/Account/AccessDenied/");
            });

            services.AddLocalization(opt => opt.ResourcesPath = "Resources");

            services.AddMvc((opt) =>
            {
                var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
                opt.Filters.Add(new AuthorizeFilter(policy));
            })
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
            .AddDataAnnotationsLocalization(opt => opt.DataAnnotationLocalizerProvider = (type, factory) =>
            {
                var assemblyName = new AssemblyName(typeof(Resources.SharedResource).GetTypeInfo().Assembly.FullName);
                return factory.Create("SharedResource", assemblyName.Name);
            })
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            //// 添加跨域的配置
            //services.Configure<ApiCorsOriginsSetting>(Configuration.GetSection("CorsOrigins"));
            //ApiCorsOriginsSetting originsSetting = null;
            //using (var scope = services.BuildServiceProvider().CreateScope())
            //{
            //    originsSetting = scope.ServiceProvider.GetRequiredService<IOptions<ApiCorsOriginsSetting>>().Value;
            //};
            //添加cors 服务
            services.AddCors(opt => opt.AddDefaultPolicy(p =>
            {
                p.AllowAnyOrigin()
                .AllowCredentials()
                .AllowAnyMethod()
                .AllowAnyHeader();
            }));

            services.Configure<RequestLocalizationOptions>(
                opt =>
                {
                    var supportedCultures = new List<CultureInfo>
                    {
                        new CultureInfo("zh"),
                        new CultureInfo("zh-CN"),
                        new CultureInfo("zh-Hans-CN"),
                        new CultureInfo("en"),
                        new CultureInfo("en-US"),
                    };
                    opt.DefaultRequestCulture = new RequestCulture(culture: "zh-Hans-CN", uiCulture: "zh-Hans-CN");
                    opt.SupportedCultures = supportedCultures;
                    opt.SupportedUICultures = supportedCultures;

                    // You can change which providers are configured to determine the culture for requests, or even add a custom
                    // provider with your own logic. The providers will be asked in order to provide a culture for each request,
                    // and the first to provide a non-null result that is in the configured supported cultures list will be used.
                    // By default, the following built-in providers are configured:
                    // - QueryStringRequestCultureProvider, sets culture via "culture" and "ui-culture" query string values, useful for testing
                    // - CookieRequestCultureProvider, sets culture via "ASPNET_CULTURE" cookie
                    // - AcceptLanguageHeaderRequestCultureProvider, sets culture via the "Accept-Language" request header
                    //opt.RequestCultureProviders.Insert(0, new QueryStringRequestCultureProvider());
                });

#if DEBUG
            // Register the Swagger generator, defining 1 or more Swagger documents
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info
                {
                    Version = "v1",
                    Title = "EsonWeb API",
                    Description = "EsonWeb ASP.NET Core Web API",
                    TermsOfService = "None",
                });

                // Set the comments path for the Swagger JSON and UI.
                var xmlFiles = Directory.EnumerateFiles(AppContext.BaseDirectory, "Eson.*.xml");
                foreach (string xmlFile in xmlFiles)
                {
                    c.IncludeXmlComments(xmlFile);
                }
            });
#endif
            // DataTables
            services.RegisterDataTables((bindingContext) => DataTablesHelper.DataTablesParametersParser(bindingContext), true);

            services.AddSignalR();

            // 添加翻译的配置
            services.Configure<TranslateSetting>(Configuration.GetSection("TranslateClient"));
            // 添加翻译的相关依赖
            services.AddTranslate((sp, opt) =>
            {
                TranslateSetting ts = sp.GetRequiredService<IOptions<TranslateSetting>>().Value;
                if (ts != null)
                {
                    opt.Options.RemoteAddress = ts.RemoteAddress;
                    opt.Options.BindingOptions.OpenTimeout = ts.OpenTimeout;
                    opt.Options.BindingOptions.CloseTimeout = ts.CloseTimeout;
                    opt.Options.BindingOptions.SendTimeout = ts.SendTimeout;
                    opt.Options.BindingOptions.ReceiveTimeout = ts.ReceiveTimeout;
                }
            });

            // 添加文件翻译的配置
            services.Configure<TranslateFileSetting>(Configuration.GetSection("TranslateFileClient"));
            // 添加文件翻译的相关依赖
            services.AddTranslateFile((sp, opt) =>
            {
                TranslateFileSetting ts = sp.GetRequiredService<IOptions<TranslateFileSetting>>().Value;
                if (ts != null)
                {
                    opt.Options.RemoteAddress = ts.RemoteAddress;
                    opt.Options.BindingOptions.OpenTimeout = ts.OpenTimeout;
                    opt.Options.BindingOptions.CloseTimeout = ts.CloseTimeout;
                    opt.Options.BindingOptions.SendTimeout = ts.SendTimeout;
                    opt.Options.BindingOptions.ReceiveTimeout = ts.ReceiveTimeout;
                }
            });

            // 添加记忆库wcf客户端的配置
            services.Configure<MemoryLibWcfClientSetting>(Configuration.GetSection("MemoryLibClient"));
            // 添加记忆库wcf客户端的相关依赖
            services.AddMemoryLibClient((sp, opt) =>
            {
                MemoryLibWcfClientSetting ts = sp.GetRequiredService<IOptions<MemoryLibWcfClientSetting>>().Value;
                if (ts != null)
                {
                    opt.Options.RemoteAddress = ts.RemoteAddress;
                    opt.Options.BindingOptions.OpenTimeout = ts.OpenTimeout;
                    opt.Options.BindingOptions.CloseTimeout = ts.CloseTimeout;
                    opt.Options.BindingOptions.SendTimeout = ts.SendTimeout;
                    opt.Options.BindingOptions.ReceiveTimeout = ts.ReceiveTimeout;
                }
            });

            // 添加软件的配置
            services.Configure<SoftwareSetting>(Configuration.GetSection("Software"));

            // 添加社交(及时聊)
            services.AddSingleton<IUserRelationManager<MySocialUser, MySocialGroup, Guid>, UserRelationManager<MySocialUser, MySocialGroup, Guid>>();
            services.AddSingleton<IUsersActionHandler<MySocialUser, MySocialGroup, Guid>, UsersActionHandler<MySocialUser, MySocialGroup, Guid, ChatHub>>();

            // 添加进度
            services.AddScoped<IProgressReporter<ProgressDto>, ProgressReporter<ProgressDto, ProgressHub>>();

            // Add application services.
            services.AddScoped<IUserIdentityAccessor, UserIdentityAccessor>();
            services.AddTransient<IEmailSender, EmailSender>();
            services.AddScoped<IUserService, UserService>();
            services.AddScoped<IEnDeCryptService, EnDeCryptService>();
            services.AddScoped<IOrganizationService, OrganizationService>();
            services.AddScoped<ICustomerService, CustomerService>();
            services.AddScoped<IChatService, ChatService>();
            services.AddScoped<IMemoryLibService, MemoryLibService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            // 创建数据库
            using (var scope = app.ApplicationServices.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
                context.Database.EnsureCreated();
            }

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseExceptionHandler(new ExceptionHandlerOptions
                {
                    ExceptionHandler = new ApiExceptionMiddleware().Invoke
                });
                app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
                {
                    HotModuleReplacement = true
                });
                app.UseDatabaseErrorPage();
            }
            else
            {
                //app.UseExceptionHandler("/Home/Error");
                app.UseExceptionHandler(new ExceptionHandlerOptions
                {
                    ExceptionHandler = new ApiExceptionMiddleware().Invoke
                });
                app.UseHsts();
            }

            // Request 使用本地化
            app.UseRequestLocalization(app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value);

            //app.UseHttpsRedirection();
            //app.UseDefaultFiles("/UI/module/home.html");
            app.UseStaticFiles();
            if (Directory.Exists(DirectoryHelper.ClientUIDir))
            {
                app.UseStaticFiles(new StaticFileOptions
                {
                    FileProvider = new PhysicalFileProvider(DirectoryHelper.ClientUIDir),
                    RequestPath = "/UI",
                });
            }
#if DEBUG
            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "EsonWeb API");
            });
#endif
            //配置Cors
            //app.UseCors(M.ApiCors);
            app.UseCors();
            app.UseWebSockets();
            app.UseCookiePolicy();

            app.UseAuthentication();

            app.UseSignalR(routes =>
            {
                routes.MapHub<ChatHub>("/test/testchat");
                routes.MapHub<ProgressHub>("/test/progress");
            });

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

                routes.MapSpaFallbackRoute(
                    name: "spa-fallback",
                    defaults: new { controller = "Home", action = "Index" });
            });
        }
    }

    public class ApiExceptionMiddleware
    {
        public async Task Invoke(HttpContext context)
        {
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            if (context.Request.Path.StartsWithSegments("/api"))
            {
                var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
                if (ex == null)
                {
                    return;
                }
                List<string> megs = new List<string>();
                GetInnerException(ex, ref megs);
                var error = new
                {
                    url = context.Request.Path,
                    method = context.Request.Method,
                    message = ex.Message,
                    messages = megs,
                };

                context.Response.ContentType = "application/json";

                using (var writer = new StreamWriter(context.Response.Body))
                {
                    new JsonSerializer().Serialize(writer, error);
                    await writer.FlushAsync().ConfigureAwait(false);
                }
            }
        }

        private Exception GetInnerException(Exception ex, ref List<string> megs)
        {
            megs.Add(ex.Message);
            if (ex.InnerException != null)
            {
                return GetInnerException(ex.InnerException, ref megs);
            }
            return ex;
        }
    }
}
duuliy commented 6 years ago

My configuration has been sent up. Can you help me to see if there is any problem? Thank you, the most handsome old man. https://github.com/aspnet/SignalR/issues/2771

------------------ 原始邮件 ------------------ 发件人: "David Fowler"notifications@github.com; 发送时间: 2018年8月9日(星期四) 中午11:36 收件人: "aspnet/SignalR"SignalR@noreply.github.com; 抄送: "转儿来"715181149@qq.com; "Author"author@noreply.github.com; 主题: Re: [aspnet/SignalR] Cross domain problem (#2771)

https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

davidfowl commented 6 years ago

What error are you getting?

duuliy commented 6 years ago

There is no problem when homology does not require cross domain. However, cross domain development environment can not get the value of ProgressReported callback, and the time to connect to signalr will be very long. The print signalr description is successfully referenced. The client code is as follows: var connection; // 进度处理 console.log(signalR) connection = new signalR.HubConnectionBuilder() .configureLogging(signalR.LogLevel.Warning) .withUrl("/test/progress", signalR.HttpTransportType.WebSockets) .build();

    connection.on("ProgressReported", function(p) {
      alert(123123);
      console.log(p)
    });

  connection.start().catch(function(err) {
    console.log("建立连接异常:" + err)
  });
duuliy commented 6 years ago

There is no problem when homology does not require cross domain. However, cross domain development environment can not get the value of ProgressReported callback, and the time to connect to signalr will be very long. The print signalr description is successfully referenced. The client code is as follows: var connection; // 进度处理 console.log(signalR) connection = new signalR.HubConnectionBuilder() .configureLogging(signalR.LogLevel.Warning) .withUrl("/test/progress", signalR.HttpTransportType.WebSockets) .build(); connection.on("ProgressReported", function(p) { alert(123123); console.log(p) }); connection.start().catch(function(err) { console.log("建立连接异常:" + err) });

------------------ 原始邮件 ------------------ 发件人: "David Fowler"notifications@github.com; 发送时间: 2018年8月9日(星期四) 中午12:09 收件人: "aspnet/SignalR"SignalR@noreply.github.com; 抄送: "转儿来"715181149@qq.com; "Author"author@noreply.github.com; 主题: Re: [aspnet/SignalR] Cross domain problem (#2771)

What error are you getting?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

davidfowl commented 6 years ago

So everything works but it just takes long? What do the logs say? Turn on debug logs.

duuliy commented 6 years ago

Thanks, it has been solved, that is, we can't use webpack's proxy to cross domain.

analogrelay commented 6 years ago

Glad you figured out the problem, I'm going to close this issue. Feel free to open a new issue if something else comes up.