Skip to content

Commit 0f4d1e4

Browse files
committed
Refactor Program.cs
1 parent 212c2f0 commit 0f4d1e4

File tree

7 files changed

+225
-134
lines changed

7 files changed

+225
-134
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using AdvancedTodoList.Application.Services.Definitions;
2+
using AdvancedTodoList.Application.Services.Definitions.Auth;
3+
using AdvancedTodoList.Application.Services.Implementations;
4+
using AdvancedTodoList.Application.Services.Implementations.Auth;
5+
using AdvancedTodoList.Core.Models.Auth;
6+
using AdvancedTodoList.Core.Models.TodoLists;
7+
using AdvancedTodoList.Core.Models.TodoLists.Members;
8+
using AdvancedTodoList.Core.Repositories;
9+
using AdvancedTodoList.Infrastructure.Repositories;
10+
11+
namespace AdvancedTodoList.WebApp.Extensions.ServiceCollection;
12+
13+
public static class DependencyInjectionExtensions
14+
{
15+
public static IServiceCollection AddDataAccessServices(this IServiceCollection services)
16+
{
17+
return services
18+
.AddScoped<IRepository<TodoList, string>, TodoListRepository>()
19+
.AddScoped<IRepository<TodoItem, int>, TodoItemsRepository>()
20+
.AddScoped<IRepository<TodoItemCategory, int>, TodoItemCategoriesRepository>()
21+
.AddScoped<IRepository<InvitationLink, int>, InvitationLinksRepository>()
22+
.AddScoped<IInvitationLinksRepository, InvitationLinksRepository>()
23+
.AddScoped<ITodoListMembersRepository, TodoListMembersRepository>()
24+
.AddScoped<IRepository<TodoListMember, int>, TodoListMembersRepository>()
25+
.AddScoped<IRepository<TodoListRole, int>, TodoListRolesRepository>()
26+
.AddScoped<IRepository<UserRefreshToken, int>, UserRefreshTokensRepository>()
27+
.AddScoped<IUserRefreshTokensRepository, UserRefreshTokensRepository>()
28+
.AddScoped<IUnitOfWork, UnitOfWork>();
29+
}
30+
31+
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
32+
{
33+
return services
34+
.AddScoped<IAuthService, AuthService>()
35+
.AddScoped<IPermissionsChecker, PermissionsChecker>()
36+
.AddScoped<ITodoListsService, TodoListsService>()
37+
.AddScoped<ITodoItemsService, TodoItemsService>()
38+
.AddScoped<ITodoItemCategoriesService, TodoItemCategoriesService>()
39+
.AddScoped<ITodoListMembersService, TodoListMembersService>()
40+
.AddScoped<ITodoListRolesService, TodoListRolesService>()
41+
.AddScoped<IInvitationLinksService, InvitationLinksService>()
42+
.AddScoped<IEntityExistenceChecker, EntityExistenceChecker>()
43+
.AddSingleton<IAccessTokensService, AccessTokensService>()
44+
.AddScoped<IRefreshTokensService, RefreshTokensService>()
45+
.AddScoped(typeof(ITodoListDependantEntitiesService<,>), typeof(TodoListDependantEntitiesService<,>));
46+
}
47+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using Microsoft.OpenApi.Models;
2+
using System.Reflection;
3+
using System.Text.Json.Serialization;
4+
5+
namespace AdvancedTodoList.WebApp.Extensions.ServiceCollection;
6+
7+
public static class EndpointExtensions
8+
{
9+
public static IServiceCollection AddEndpoints(this IServiceCollection services)
10+
{
11+
// Add services to the container.
12+
services.AddControllers()
13+
.AddJsonOptions(options =>
14+
{
15+
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
16+
});
17+
18+
return services.AddEndpointsApiExplorer();
19+
}
20+
21+
public static IServiceCollection AddApplicationSwagger(this IServiceCollection services)
22+
{
23+
return services.AddSwaggerGen(options =>
24+
{
25+
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
26+
{
27+
In = ParameterLocation.Header,
28+
Description = "Please enter access token (JWT)",
29+
Name = "Authorization",
30+
Type = SecuritySchemeType.ApiKey
31+
});
32+
33+
var scheme = new OpenApiSecurityScheme
34+
{
35+
Reference = new OpenApiReference
36+
{
37+
Type = ReferenceType.SecurityScheme,
38+
Id = "Bearer"
39+
}
40+
};
41+
42+
options.AddSecurityRequirement(new OpenApiSecurityRequirement { { scheme, Array.Empty<string>() } });
43+
44+
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
45+
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
46+
});
47+
}
48+
49+
public static IServiceCollection AddApplicationAntiforgery(this IServiceCollection services)
50+
{
51+
// Configure antiforgery
52+
return services.AddAntiforgery(options => options.HeaderName = "X-XSRF-Token");
53+
}
54+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using AdvancedTodoList.Application.Validation;
2+
using FluentValidation;
3+
using SharpGrip.FluentValidation.AutoValidation.Mvc.Extensions;
4+
5+
namespace AdvancedTodoList.WebApp.Extensions.ServiceCollection;
6+
7+
public static class ValidationExtensions
8+
{
9+
public static IServiceCollection AddFluentValidation(this IServiceCollection services)
10+
{
11+
ValidatorOptions.Global.LanguageManager.Enabled = false; // Disable localization
12+
return services.AddValidatorsFromAssemblyContaining<TodoItemCreateDtoValidator>();
13+
}
14+
15+
public static IServiceCollection AddFluentValidationAutoValidation(this IServiceCollection services)
16+
{
17+
return services.AddFluentValidationAutoValidation(configuration =>
18+
{
19+
// Disable the built-in .NET model (data annotations) validation.
20+
configuration.DisableBuiltInModelValidation = true;
21+
// Enable validation for parameters bound from 'BindingSource.Body' binding sources.
22+
configuration.EnableBodyBindingSourceAutomaticValidation = true;
23+
// Enable validation for parameters bound from 'BindingSource.Query' binding sources.
24+
configuration.EnableQueryBindingSourceAutomaticValidation = true;
25+
});
26+
}
27+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Microsoft.IdentityModel.Tokens;
2+
using System.Text;
3+
4+
namespace AdvancedTodoList.WebApp.Extensions.WebAppBuilder;
5+
6+
public static class AuthExtensions
7+
{
8+
public static WebApplicationBuilder AddAuth(this WebApplicationBuilder builder)
9+
{
10+
// Configure auth
11+
string? jwtSecret = builder.Configuration["Auth:AccessToken:SecretKey"] ??
12+
throw new InvalidOperationException("JWT secret is not configured");
13+
builder.Services.AddAuthentication()
14+
.AddJwtBearer(options =>
15+
{
16+
options.SaveToken = true;
17+
options.TokenValidationParameters = new TokenValidationParameters
18+
{
19+
ClockSkew = TimeSpan.FromSeconds(5),
20+
ValidIssuer = builder.Configuration["Auth:AccessToken:ValidIssuer"],
21+
ValidAudience = builder.Configuration["Auth:AccessToken:ValidAudience"],
22+
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret))
23+
};
24+
});
25+
builder.Services.AddAuthorizationBuilder();
26+
27+
return builder;
28+
}
29+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using AdvancedTodoList.Core.Models.Auth;
2+
using AdvancedTodoList.Infrastructure.Data;
3+
using Microsoft.AspNetCore.Identity;
4+
using Microsoft.EntityFrameworkCore;
5+
6+
namespace AdvancedTodoList.WebApp.Extensions.WebAppBuilder;
7+
8+
public static class DataAccessExtensions
9+
{
10+
public static WebApplicationBuilder AddDataAccess(this WebApplicationBuilder builder)
11+
{
12+
// Get connection string
13+
string? connectionString =
14+
builder.Configuration.GetConnectionString("DefaultConnection") ??
15+
throw new InvalidOperationException("Connection string is not specified in 'ConnectionStrings:DefaultConnection'");
16+
17+
// Configure DbContext
18+
builder.Services.AddDbContext<ApplicationDbContext>(
19+
o => o.UseSqlServer(connectionString,
20+
b => b.MigrationsAssembly("AdvancedTodoList.Infrastructure"))
21+
);
22+
23+
builder.Services.AddIdentityCore<ApplicationUser>(options =>
24+
{
25+
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
26+
})
27+
.AddEntityFrameworkStores<ApplicationDbContext>()
28+
.AddApiEndpoints();
29+
30+
return builder;
31+
}
32+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using AdvancedTodoList.Application.Options;
2+
3+
namespace AdvancedTodoList.WebApp.Extensions.WebAppBuilder;
4+
5+
public static class OptionsExtensions
6+
{
7+
public static WebApplicationBuilder ConfigureApplicationOptions(this WebApplicationBuilder builder)
8+
{
9+
builder.Services
10+
.Configure<AccessTokenOptions>(
11+
builder.Configuration.GetSection("Auth:AccessToken"))
12+
.Configure<RefreshTokenOptions>(
13+
builder.Configuration.GetSection("Auth:RefreshToken"))
14+
.Configure<InvitationLinkOptions>(
15+
builder.Configuration.GetSection("Todo:InvitationLink")
16+
);
17+
18+
return builder;
19+
}
20+
}

AdvancedTodoList/Program.cs

Lines changed: 16 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,33 @@
11
using AdvancedTodoList.Application.Mapping;
2-
using AdvancedTodoList.Application.Options;
3-
using AdvancedTodoList.Application.Services.Definitions;
4-
using AdvancedTodoList.Application.Services.Definitions.Auth;
5-
using AdvancedTodoList.Application.Services.Implementations;
6-
using AdvancedTodoList.Application.Services.Implementations.Auth;
7-
using AdvancedTodoList.Application.Validation;
8-
using AdvancedTodoList.Core.Models.Auth;
9-
using AdvancedTodoList.Core.Models.TodoLists;
10-
using AdvancedTodoList.Core.Models.TodoLists.Members;
11-
using AdvancedTodoList.Core.Repositories;
12-
using AdvancedTodoList.Infrastructure.Data;
13-
using AdvancedTodoList.Infrastructure.Repositories;
14-
using FluentValidation;
15-
using Microsoft.AspNetCore.Identity;
16-
using Microsoft.EntityFrameworkCore;
17-
using Microsoft.IdentityModel.Tokens;
18-
using Microsoft.OpenApi.Models;
2+
using AdvancedTodoList.WebApp.Extensions.ServiceCollection;
3+
using AdvancedTodoList.WebApp.Extensions.WebAppBuilder;
194
using SharpGrip.FluentValidation.AutoValidation.Mvc.Extensions;
20-
using System.Reflection;
21-
using System.Text;
22-
using System.Text.Json.Serialization;
235

246
var builder = WebApplication.CreateBuilder(args);
257

26-
// Add services to the container.
27-
builder.Services.AddControllers()
28-
.AddJsonOptions(options =>
29-
{
30-
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
31-
});
32-
builder.Services.AddEndpointsApiExplorer();
33-
builder.Services.AddSwaggerGen(options =>
34-
{
35-
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
36-
{
37-
In = ParameterLocation.Header,
38-
Description = "Please enter access token (JWT)",
39-
Name = "Authorization",
40-
Type = SecuritySchemeType.ApiKey
41-
});
42-
43-
var scheme = new OpenApiSecurityScheme
44-
{
45-
Reference = new OpenApiReference
46-
{
47-
Type = ReferenceType.SecurityScheme,
48-
Id = "Bearer"
49-
}
50-
};
51-
52-
options.AddSecurityRequirement(new OpenApiSecurityRequirement { { scheme, Array.Empty<string>() } });
53-
54-
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
55-
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
56-
});
57-
58-
// Configure antiforgery
59-
builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-Token");
60-
61-
// Configure auth
62-
string? jwtSecret = builder.Configuration["Auth:AccessToken:SecretKey"] ??
63-
throw new InvalidOperationException("JWT secret is not configured");
64-
builder.Services.AddAuthentication()
65-
.AddJwtBearer(options =>
66-
{
67-
options.SaveToken = true;
68-
options.TokenValidationParameters = new TokenValidationParameters
69-
{
70-
ClockSkew = TimeSpan.FromSeconds(5),
71-
ValidIssuer = builder.Configuration["Auth:AccessToken:ValidIssuer"],
72-
ValidAudience = builder.Configuration["Auth:AccessToken:ValidAudience"],
73-
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret))
74-
};
75-
});
76-
builder.Services.AddAuthorizationBuilder();
8+
builder.Services
9+
.AddEndpoints()
10+
.AddApplicationSwagger()
11+
.AddApplicationAntiforgery();
7712

78-
// Get connection string
79-
string? connectionString =
80-
builder.Configuration.GetConnectionString("DefaultConnection") ??
81-
throw new InvalidOperationException("Connection string is not specified in 'ConnectionStrings:DefaultConnection'");
82-
83-
// Configure DbContext
84-
builder.Services.AddDbContext<ApplicationDbContext>(
85-
o => o.UseSqlServer(connectionString,
86-
b => b.MigrationsAssembly("AdvancedTodoList.Infrastructure"))
87-
);
88-
89-
builder.Services.AddIdentityCore<ApplicationUser>(options =>
90-
{
91-
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
92-
})
93-
.AddEntityFrameworkStores<ApplicationDbContext>()
94-
.AddApiEndpoints();
13+
builder.AddAuth();
14+
builder.AddDataAccess();
9515

9616
// Bind options
97-
builder.Services.Configure<AccessTokenOptions>(
98-
builder.Configuration.GetSection("Auth:AccessToken"));
99-
builder.Services.Configure<RefreshTokenOptions>(
100-
builder.Configuration.GetSection("Auth:RefreshToken"));
101-
builder.Services.Configure<InvitationLinkOptions>(
102-
builder.Configuration.GetSection("Todo:InvitationLink"));
17+
builder.ConfigureApplicationOptions();
10318

10419
// Register application services
105-
builder.Services.AddScoped<IAuthService, AuthService>();
106-
builder.Services.AddScoped<IPermissionsChecker, PermissionsChecker>();
107-
builder.Services.AddScoped<ITodoListsService, TodoListsService>();
108-
builder.Services.AddScoped<ITodoItemsService, TodoItemsService>();
109-
builder.Services.AddScoped<ITodoItemCategoriesService, TodoItemCategoriesService>();
110-
builder.Services.AddScoped<ITodoListMembersService, TodoListMembersService>();
111-
builder.Services.AddScoped<ITodoListRolesService, TodoListRolesService>();
112-
builder.Services.AddScoped<IInvitationLinksService, InvitationLinksService>();
113-
builder.Services.AddScoped<IEntityExistenceChecker, EntityExistenceChecker>();
114-
builder.Services.AddSingleton<IAccessTokensService, AccessTokensService>();
115-
builder.Services.AddScoped<IRefreshTokensService, RefreshTokensService>();
116-
builder.Services.AddScoped(typeof(ITodoListDependantEntitiesService<,>),
117-
typeof(TodoListDependantEntitiesService<,>));
118-
// Register unit of work
119-
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
120-
// Register application repositories
121-
builder.Services.AddScoped<IRepository<TodoList, string>, TodoListRepository>();
122-
builder.Services.AddScoped<IRepository<TodoItem, int>, TodoItemsRepository>();
123-
builder.Services.AddScoped<IRepository<TodoItemCategory, int>, TodoItemCategoriesRepository>();
124-
builder.Services.AddScoped<IRepository<InvitationLink, int>, InvitationLinksRepository>();
125-
builder.Services.AddScoped<IInvitationLinksRepository, InvitationLinksRepository>();
126-
builder.Services.AddScoped<ITodoListMembersRepository, TodoListMembersRepository>();
127-
builder.Services.AddScoped<IRepository<TodoListMember, int>, TodoListMembersRepository>();
128-
builder.Services.AddScoped<IRepository<TodoListRole, int>, TodoListRolesRepository>();
129-
builder.Services.AddScoped<IRepository<UserRefreshToken, int>, UserRefreshTokensRepository>();
130-
builder.Services.AddScoped<IUserRefreshTokensRepository, UserRefreshTokensRepository>();
20+
builder.Services
21+
.AddDataAccessServices()
22+
.AddApplicationServices();
13123

13224
// Apply mapping settings
13325
MappingGlobalSettings.Apply();
13426

13527
// Add fluent validation
136-
ValidatorOptions.Global.LanguageManager.Enabled = false; // Disable localization
137-
builder.Services.AddValidatorsFromAssemblyContaining<TodoItemCreateDtoValidator>();
138-
139-
// Enable auto validation by SharpGrip
140-
builder.Services.AddFluentValidationAutoValidation(configuration =>
141-
{
142-
// Disable the built-in .NET model (data annotations) validation.
143-
configuration.DisableBuiltInModelValidation = true;
144-
// Enable validation for parameters bound from 'BindingSource.Body' binding sources.
145-
configuration.EnableBodyBindingSourceAutomaticValidation = true;
146-
// Enable validation for parameters bound from 'BindingSource.Query' binding sources.
147-
configuration.EnableQueryBindingSourceAutomaticValidation = true;
148-
});
149-
28+
builder.Services
29+
.AddFluentValidation()
30+
.AddFluentValidationAutoValidation();
15031

15132
var app = builder.Build();
15233

@@ -164,4 +45,5 @@
16445
app.MapControllers();
16546

16647
app.Run();
48+
16749
public partial class Program { }

0 commit comments

Comments
 (0)