· 6 years ago · Oct 16, 2019, 10:14 AM
1using System;
2using System.Net;
3using System.Reflection;
4using System.Text;
5using System.Threading.Tasks;
6using Autofac;
7using Autofac.Extensions.DependencyInjection;
8using AutoMapper;
9using Core;
10using Core.BackgroundJobs;
11using Core.Domain.Entities;
12using Core.Interfaces;
13using Infrastructure;
14using Infrastructure.Auth;
15using Infrastructure.Data;
16using Infrastructure.Helpers;
17using Microsoft.AspNetCore.Authentication.JwtBearer;
18using Microsoft.AspNetCore.Builder;
19using Microsoft.AspNetCore.Diagnostics;
20using Microsoft.AspNetCore.Hosting;
21using Microsoft.AspNetCore.Http;
22using Microsoft.AspNetCore.Http.Features;
23using Microsoft.AspNetCore.Identity;
24using Microsoft.AspNetCore.Mvc;
25using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
26using Microsoft.EntityFrameworkCore;
27using Microsoft.Extensions.Configuration;
28using Microsoft.Extensions.DependencyInjection;
29using Microsoft.Extensions.Hosting;
30using Microsoft.IdentityModel.Tokens;
31using Swashbuckle.AspNetCore.Swagger;
32using Web.Extensions;
33using Web.Hubs;
34using Web.Models.Settings;
35using Web.Service;
36using Web.Utils;
37
38namespace Legal
39{
40 public class Startup
41 {
42 public IConfiguration Configuration { get; }
43 public IWebHostEnvironment Env { get; }
44 public Startup(IConfiguration configuration, IWebHostEnvironment env) => (Configuration, Env) = (configuration, env);
45 public IServiceProvider ConfigureServices(IServiceCollection services)
46 {
47 ConfigureContext(services);
48 var appSettings = ConfigureAppSettings(services);
49 ConfigureAuth(services, appSettings);
50 ConfigureWeb(services);
51 ConfigureBackgroundJobs(services);
52 ConfigureSwagger(services);
53 return ConfigureContainer(services, appSettings);
54 }
55
56 public void Configure(
57 IApplicationBuilder app,
58 IWebHostEnvironment env,
59 UserManager<User> userManager,
60 RoleManager<Role> roleManager)
61 {
62 app.UseExceptionHandler(
63 builder =>
64 {
65 builder.Run(
66 async context =>
67 {
68 context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
69 context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
70
71 var error = context.Features.Get<IExceptionHandlerFeature>();
72 if (error != null)
73 {
74 context.Response.AddApplicationError(error.Error.Message);
75 await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
76 }
77 });
78 });
79
80 if (env.IsDevelopment())
81 {
82 app.UseSwagger();
83 app.UseSwaggerUI(c =>
84 {
85 c.SwaggerEndpoint("v1/swagger.json", "LegalAPI v1");
86 });
87 }
88 else
89 {
90 app.UseHsts();
91 }
92
93 if (env.IsProduction())
94 {
95 app.UseHttpsRedirection();
96 }
97
98 app.UseAuthorization();
99 app.UseAuthentication();
100
101 app.UseResponseCompression();
102
103 var webSocketOptions = new WebSocketOptions()
104 {
105 KeepAliveInterval = TimeSpan.FromSeconds(120),
106 ReceiveBufferSize = 4 * 1024
107 };
108
109 app.UseWebSockets(webSocketOptions);
110
111 app.UseFileServer();
112 app.UseSpaStaticFiles();
113 app.UseRouting();
114
115 // IdentityDataInitializer.SeedData(userManager, roleManager);
116
117 app.UseEndpoints(routes =>
118 {
119 routes.MapControllerRoute(
120 name: "default",
121 pattern: "{controller}/{action=Index}/{id?}");
122 routes.MapHub<NotificationHub>("/hub/notification");
123 });
124
125 app.UseSpa(spa =>
126 {
127 spa.Options.SourcePath = "wwwroot";
128
129 if (env.IsDevelopment())
130 {
131 spa.UseReactDevelopmentServer(npmScript: "start");
132 }
133 });
134 }
135
136 private void ConfigureContext(IServiceCollection services)
137 {
138 services.AddDbContextPool<AppDbContext>(options =>
139 options.UseSqlServer(Configuration["TestConnection"],
140 b =>
141 {
142 b.MigrationsAssembly("Infrastructure");
143 b.EnableRetryOnFailure();
144 }));
145 services.AddIdentity<User, Role>(o =>
146 {
147 o.Stores.MaxLengthForKeys = 128;
148 o.Password.RequireDigit = false;
149 o.Password.RequireLowercase = false;
150 o.Password.RequireUppercase = false;
151 o.Password.RequireNonAlphanumeric = false;
152 o.Password.RequiredLength = 6;
153 }).AddEntityFrameworkStores<AppDbContext>().AddDefaultTokenProviders();
154 }
155
156 private AppSettings ConfigureAppSettings(IServiceCollection services)
157 {
158 var appSettings = new AppSettings
159 {
160 SecretKey = Configuration["JwtKey"],
161 SendGridApiKey = Configuration["SendGridApiKey"],
162 EmailSenderAddress = Configuration["EmailSenderAddress"],
163 EmailSenderName = Configuration["EmailSenderName"],
164 Country = Configuration["Legal:Country"],
165 BaseUrl = Configuration["BaseUrl"],
166 SupportEmail = Configuration["SupportEmail"]
167 };
168 services.AddSingleton(appSettings);
169 return appSettings;
170 }
171
172 private void ConfigureAuth(IServiceCollection services, AppSettings appSettings)
173 {
174 var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(appSettings.SecretKey));
175
176 var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
177
178 services.Configure<JwtIssuerOptions>(options =>
179 {
180 options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
181 options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
182 options.SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
183 });
184
185 var tokenValidationParameters = new TokenValidationParameters
186 {
187 ValidateIssuer = true,
188 ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
189
190 ValidateAudience = true,
191 ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
192
193 ValidateIssuerSigningKey = true,
194 IssuerSigningKey = signingKey,
195
196 RequireExpirationTime = false,
197 ValidateLifetime = true,
198 ClockSkew = TimeSpan.Zero
199 };
200
201 services.AddAuthentication(options =>
202 {
203 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
204 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
205
206 }).AddJwtBearer(configureOptions =>
207 {
208 configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
209 configureOptions.TokenValidationParameters = tokenValidationParameters;
210 configureOptions.SaveToken = true;
211
212 configureOptions.Events = new JwtBearerEvents
213 {
214 OnMessageReceived = context =>
215 {
216 var accessToken = context.Request.Query["access_token"];
217
218 var path = context.HttpContext.Request.Path;
219 if (!string.IsNullOrEmpty(accessToken) &&
220 (path.StartsWithSegments("/hub")))
221 {
222 context.Token = accessToken;
223 }
224 return Task.CompletedTask;
225 },
226 OnAuthenticationFailed = context =>
227 {
228 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
229 {
230 context.Response.Headers.Add("Token-Expired", "true");
231 }
232 return Task.CompletedTask;
233 }
234 };
235 });
236
237 services.AddAuthorization(options =>
238 {
239 options.AddPolicy("EmailConfirmed", policy => policy.RequireClaim(Constants.Strings.JwtClaims.EmailConfirmed, "true"));
240 });
241 }
242
243 private void ConfigureWeb(IServiceCollection services)
244 {
245 if (Env.IsProduction())
246 {
247 services.AddHsts(options =>
248 {
249 options.Preload = true;
250 options.IncludeSubDomains = true;
251 options.MaxAge = TimeSpan.FromDays(60);
252 });
253
254 services.AddHttpsRedirection(options =>
255 {
256 options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
257 });
258 }
259
260 services.AddMemoryCache();
261 services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
262
263 services.AddSignalR();
264 services.AddMvc()
265 .SetCompatibilityVersion(CompatibilityVersion.Latest);
266 .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
267
268 services.Configure<FormOptions>(x =>
269 {
270 x.ValueLengthLimit = int.MaxValue;
271 x.MultipartBodyLengthLimit = int.MaxValue;
272 });
273 services.AddSpaStaticFiles(configuration =>
274 {
275 configuration.RootPath = "wwwroot/build";
276 });
277
278 services.AddResponseCompression();
279 }
280
281 private void ConfigureSwagger(IServiceCollection services)
282 {
283 services.AddSwaggerGen(c =>
284 {
285 c.SwaggerDoc("v1", new Info { Title = "LegalAPI", Version = "v1" });
286 });
287 }
288
289 private AutofacServiceProvider ConfigureContainer(IServiceCollection services, AppSettings appSettings)
290 {
291 services.AddScoped<IRazorViewToStringRenderer, RazorViewToStringRenderer>();
292 var builder = new ContainerBuilder();
293 builder.RegisterInstance(appSettings).As<IAppSettings>().SingleInstance();
294 builder.RegisterModule(new CoreModule());
295 builder.RegisterModule(new InfrastructureModule());
296 builder.RegisterType<ConnectionManager>().As<ConnectionManager>().InstancePerLifetimeScope();
297 builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Presenter")).SingleInstance();
298 builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Service")).InstancePerLifetimeScope();
299
300 builder.Populate(services);
301 var container = builder.Build();
302 return new AutofacServiceProvider(container);
303 }
304
305 private void ConfigureBackgroundJobs(IServiceCollection services)
306 {
307 services.AddHostedService<QueuedHostedService>();
308 services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
309 }
310 }
311}