@ -1,11 +0,0 @@ | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers; | |||||
[Route("")] | |||||
public class HomeController : Controller | |||||
{ | |||||
[HttpGet] | |||||
public IActionResult Index() | |||||
{ | |||||
return new RedirectResult("~/swagger"); | |||||
} | |||||
} |
@ -0,0 +1,57 @@ | |||||
public static class ServiceCollectionExtensions | |||||
{ | |||||
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddHealthChecks() | |||||
.AddUrlGroup(_ => new Uri(configuration.GetRequiredValue("CatalogUrlHC")), name: "catalogapi-check", tags: new string[] { "catalogapi" }) | |||||
.AddUrlGroup(_ => new Uri(configuration.GetRequiredValue("OrderingUrlHC")), name: "orderingapi-check", tags: new string[] { "orderingapi" }) | |||||
.AddUrlGroup(_ => new Uri(configuration.GetRequiredValue("BasketUrlHC")), name: "basketapi-check", tags: new string[] { "basketapi" }) | |||||
.AddUrlGroup(_ => new Uri(configuration.GetRequiredValue("IdentityUrlHC")), name: "identityapi-check", tags: new string[] { "identityapi" }); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddApplicationServices(this IServiceCollection services) | |||||
{ | |||||
// Register delegating handlers | |||||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); | |||||
// Register http services | |||||
services.AddHttpClient<IOrderApiClient, OrderApiClient>() | |||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>(); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddGrpcServices(this IServiceCollection services) | |||||
{ | |||||
services.AddTransient<GrpcExceptionInterceptor>(); | |||||
services.AddScoped<IBasketService, BasketService>(); | |||||
services.AddGrpcClient<Basket.BasketClient>((services, options) => | |||||
{ | |||||
var basketApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcBasket; | |||||
options.Address = new Uri(basketApi); | |||||
}).AddInterceptor<GrpcExceptionInterceptor>(); | |||||
services.AddScoped<ICatalogService, CatalogService>(); | |||||
services.AddGrpcClient<Catalog.CatalogClient>((services, options) => | |||||
{ | |||||
var catalogApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcCatalog; | |||||
options.Address = new Uri(catalogApi); | |||||
}).AddInterceptor<GrpcExceptionInterceptor>(); | |||||
services.AddScoped<IOrderingService, OrderingService>(); | |||||
services.AddGrpcClient<GrpcOrdering.OrderingGrpc.OrderingGrpcClient>((services, options) => | |||||
{ | |||||
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering; | |||||
options.Address = new Uri(orderingApi); | |||||
}).AddInterceptor<GrpcExceptionInterceptor>(); | |||||
return services; | |||||
} | |||||
} |
@ -1,34 +0,0 @@ | |||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters | |||||
{ | |||||
namespace Basket.API.Infrastructure.Filters | |||||
{ | |||||
public class AuthorizeCheckOperationFilter : IOperationFilter | |||||
{ | |||||
public void Apply(OpenApiOperation operation, OperationFilterContext context) | |||||
{ | |||||
// Check for authorize attribute | |||||
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() || | |||||
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any(); | |||||
if (!hasAuthorize) return; | |||||
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); | |||||
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); | |||||
var oAuthScheme = new OpenApiSecurityScheme | |||||
{ | |||||
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } | |||||
}; | |||||
operation.Security = new List<OpenApiSecurityRequirement> | |||||
{ | |||||
new() | |||||
{ | |||||
[ oAuthScheme ] = new[] { "Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator" } | |||||
} | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
} |
@ -1,38 +1,27 @@ | |||||
global using CatalogApi; | |||||
global using Grpc.Core.Interceptors; | |||||
global using System; | |||||
global using System.Collections.Generic; | |||||
global using System.Linq; | |||||
global using System.Net; | |||||
global using System.Net.Http; | |||||
global using System.Net.Http.Headers; | |||||
global using System.Text.Json; | |||||
global using System.Threading; | |||||
global using System.Threading.Tasks; | |||||
global using CatalogApi; | |||||
global using Grpc.Core; | global using Grpc.Core; | ||||
global using Grpc.Core.Interceptors; | |||||
global using GrpcBasket; | global using GrpcBasket; | ||||
global using HealthChecks.UI.Client; | |||||
global using Microsoft.AspNetCore.Authentication; | global using Microsoft.AspNetCore.Authentication; | ||||
global using Microsoft.AspNetCore.Authorization; | global using Microsoft.AspNetCore.Authorization; | ||||
global using Microsoft.AspNetCore.Builder; | global using Microsoft.AspNetCore.Builder; | ||||
global using Microsoft.AspNetCore.Diagnostics.HealthChecks; | |||||
global using Microsoft.AspNetCore.Hosting; | |||||
global using Microsoft.AspNetCore.Http; | global using Microsoft.AspNetCore.Http; | ||||
global using Microsoft.AspNetCore.Mvc; | global using Microsoft.AspNetCore.Mvc; | ||||
global using Microsoft.AspNetCore; | |||||
global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | ||||
global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; | |||||
global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure; | global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure; | ||||
global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | ||||
global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; | global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; | ||||
global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator; | |||||
global using Microsoft.Extensions.Configuration; | global using Microsoft.Extensions.Configuration; | ||||
global using Microsoft.Extensions.DependencyInjection; | global using Microsoft.Extensions.DependencyInjection; | ||||
global using Microsoft.Extensions.Diagnostics.HealthChecks; | |||||
global using Microsoft.Extensions.Hosting; | |||||
global using Microsoft.Extensions.Logging; | global using Microsoft.Extensions.Logging; | ||||
global using Microsoft.Extensions.Options; | global using Microsoft.Extensions.Options; | ||||
global using Microsoft.OpenApi.Models; | |||||
global using Swashbuckle.AspNetCore.SwaggerGen; | |||||
global using System.Collections.Generic; | |||||
global using System.IdentityModel.Tokens.Jwt; | |||||
global using System.Linq; | |||||
global using System.Net.Http.Headers; | |||||
global using System.Net.Http; | |||||
global using System.Net; | |||||
global using System.Text.Json; | |||||
global using System.Threading.Tasks; | |||||
global using System.Threading; | |||||
global using System; | |||||
global using Microsoft.IdentityModel.Tokens; | |||||
global using Services.Common; |
@ -1,169 +1,42 @@ | |||||
var builder = WebApplication.CreateBuilder(args); | var builder = WebApplication.CreateBuilder(args); | ||||
builder.Services.AddHealthChecks() | |||||
.AddCheck("self", () => HealthCheckResult.Healthy()) | |||||
.AddUrlGroup(new Uri(builder.Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) | |||||
.AddUrlGroup(new Uri(builder.Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) | |||||
.AddUrlGroup(new Uri(builder.Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) | |||||
.AddUrlGroup(new Uri(builder.Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) | |||||
.AddUrlGroup(new Uri(builder.Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }); | |||||
builder.Services.AddCustomMvc(builder.Configuration) | |||||
.AddCustomAuthentication(builder.Configuration) | |||||
.AddApplicationServices() | |||||
.AddGrpcServices(); | |||||
var app = builder.Build(); | |||||
if (!app.Environment.IsDevelopment()) | |||||
{ | |||||
app.UseExceptionHandler("/Home/Error"); | |||||
} | |||||
var pathBase = builder.Configuration["PATH_BASE"]; | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
builder.AddServiceDefaults(); | |||||
app.UseHttpsRedirection(); | |||||
builder.Services.AddControllers(); | |||||
app.UseSwagger().UseSwaggerUI(c => | |||||
builder.Services.AddHealthChecks(builder.Configuration); | |||||
builder.Services.AddCors(options => | |||||
{ | { | ||||
c.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Purchase BFF V1"); | |||||
c.OAuthClientId("webshoppingaggswaggerui"); | |||||
c.OAuthClientSecret(string.Empty); | |||||
c.OAuthRealm(string.Empty); | |||||
c.OAuthAppName("web shopping bff Swagger UI"); | |||||
options.AddPolicy("CorsPolicy", | |||||
builder => builder | |||||
.SetIsOriginAllowed((host) => true) | |||||
.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials()); | |||||
}); | }); | ||||
app.UseRouting(); | |||||
app.UseCors("CorsPolicy"); | |||||
app.UseAuthentication(); | |||||
app.UseAuthorization(); | |||||
builder.Services.AddApplicationServices(); | |||||
builder.Services.AddGrpcServices(); | |||||
app.MapDefaultControllerRoute(); | |||||
app.MapControllers(); | |||||
app.MapHealthChecks("/hc", new HealthCheckOptions() | |||||
{ | |||||
Predicate = _ => true, | |||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | |||||
}); | |||||
app.MapHealthChecks("/liveness", new HealthCheckOptions | |||||
{ | |||||
Predicate = r => r.Name.Contains("self") | |||||
}); | |||||
builder.Services.Configure<UrlsConfig>(builder.Configuration.GetSection("urls")); | |||||
await app.RunAsync(); | |||||
var app = builder.Build(); | |||||
public static class ServiceCollectionExtensions | |||||
if (!await app.CheckHealthAsync()) | |||||
{ | { | ||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | |||||
var identityUrl = configuration.GetValue<string>("urls:identity"); | |||||
services.AddAuthentication("Bearer") | |||||
.AddJwtBearer(options => | |||||
{ | |||||
options.Authority = identityUrl; | |||||
options.RequireHttpsMetadata = false; | |||||
options.Audience = "webshoppingagg"; | |||||
options.TokenValidationParameters = new TokenValidationParameters | |||||
{ | |||||
ValidateAudience = false | |||||
}; | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.Configure<UrlsConfig>(configuration.GetSection("urls")); | |||||
services.AddControllers() | |||||
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); | |||||
services.AddSwaggerGen(options => | |||||
{ | |||||
options.SwaggerDoc("v1", new OpenApiInfo | |||||
{ | |||||
Title = "Shopping Aggregator for Web Clients", | |||||
Version = "v1", | |||||
Description = "Shopping Aggregator for Web Clients" | |||||
}); | |||||
var identityUrl = configuration.GetSection("Identity").GetValue<string>("ExternalUrl"); | |||||
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme | |||||
{ | |||||
Type = SecuritySchemeType.OAuth2, | |||||
Flows = new OpenApiOAuthFlows() | |||||
{ | |||||
Implicit = new OpenApiOAuthFlow() | |||||
{ | |||||
AuthorizationUrl = new Uri($"{identityUrl}/connect/authorize"), | |||||
TokenUrl = new Uri($"{identityUrl}/connect/token"), | |||||
Scopes = new Dictionary<string, string>() | |||||
{ | |||||
{ "webshoppingagg", "Shopping Aggregator for Web Clients" } | |||||
} | |||||
} | |||||
} | |||||
}); | |||||
options.OperationFilter<AuthorizeCheckOperationFilter>(); | |||||
}); | |||||
services.AddCors(options => | |||||
{ | |||||
options.AddPolicy("CorsPolicy", | |||||
builder => builder | |||||
.SetIsOriginAllowed((host) => true) | |||||
.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials()); | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddApplicationServices(this IServiceCollection services) | |||||
{ | |||||
//register delegating handlers | |||||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); | |||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||||
//register http services | |||||
services.AddHttpClient<IOrderApiClient, OrderApiClient>() | |||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>(); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddGrpcServices(this IServiceCollection services) | |||||
{ | |||||
services.AddTransient<GrpcExceptionInterceptor>(); | |||||
services.AddScoped<IBasketService, BasketService>(); | |||||
return; | |||||
} | |||||
services.AddGrpcClient<Basket.BasketClient>((services, options) => | |||||
{ | |||||
var basketApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcBasket; | |||||
options.Address = new Uri(basketApi); | |||||
}).AddInterceptor<GrpcExceptionInterceptor>(); | |||||
app.UseServiceDefaults(); | |||||
services.AddScoped<ICatalogService, CatalogService>(); | |||||
app.UseHttpsRedirection(); | |||||
services.AddGrpcClient<Catalog.CatalogClient>((services, options) => | |||||
{ | |||||
var catalogApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcCatalog; | |||||
options.Address = new Uri(catalogApi); | |||||
}).AddInterceptor<GrpcExceptionInterceptor>(); | |||||
app.UseCors("CorsPolicy"); | |||||
app.UseAuthentication(); | |||||
app.UseAuthorization(); | |||||
services.AddScoped<IOrderingService, OrderingService>(); | |||||
app.MapGet("/", () => Results.Redirect("/swagger")); | |||||
services.AddGrpcClient<GrpcOrdering.OrderingGrpc.OrderingGrpcClient>((services, options) => | |||||
{ | |||||
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering; | |||||
options.Address = new Uri(orderingApi); | |||||
}).AddInterceptor<GrpcExceptionInterceptor>(); | |||||
app.MapControllers(); | |||||
return services; | |||||
} | |||||
} | |||||
await app.RunAsync(); |
@ -1,29 +1,16 @@ | |||||
{ | { | ||||
"iisSettings": { | |||||
"windowsAuthentication": false, | |||||
"anonymousAuthentication": true, | |||||
"iisExpress": { | |||||
"applicationUrl": "http://localhost:57425/", | |||||
"sslPort": 0 | |||||
} | |||||
}, | |||||
"profiles": { | "profiles": { | ||||
"IIS Express": { | |||||
"commandName": "IISExpress", | |||||
"launchBrowser": true, | |||||
"launchUrl": "api/values", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
}, | |||||
"PurchaseForMvc": { | |||||
"Web.Shopping.HttpAggregator": { | |||||
"commandName": "Project", | "commandName": "Project", | ||||
"launchBrowser": true, | "launchBrowser": true, | ||||
"launchUrl": "api/values", | |||||
"applicationUrl": "http://localhost:61632/", | |||||
"environmentVariables": { | "environmentVariables": { | ||||
"CatalogUrlHC": "http://localhost:5222/hc", | |||||
"OrderingUrlHC": "http://localhost:5224/hc", | |||||
"BasketUrlHC": "http://localhost:5221/hc", | |||||
"IdentityUrlHC": "http://localhost:5223/hc", | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | "ASPNETCORE_ENVIRONMENT": "Development" | ||||
}, | |||||
"applicationUrl": "http://localhost:61632/" | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@ -1,16 +1,8 @@ | |||||
{ | { | ||||
"Logging": { | "Logging": { | ||||
"Debug": { | |||||
"IncludeScopes": false, | |||||
"LogLevel": { | |||||
"Default": "Debug" | |||||
} | |||||
}, | |||||
"Console": { | |||||
"IncludeScopes": false, | |||||
"LogLevel": { | |||||
"Default": "Debug" | |||||
} | |||||
"LogLevel": { | |||||
"Default": "Information", | |||||
"Microsoft.AspNetCore": "Warning" | |||||
} | } | ||||
} | } | ||||
} | } |
@ -1,20 +1,29 @@ | |||||
{ | { | ||||
"Identity": { | |||||
"Url": "http://localhost:5105", | |||||
"Audience": "webshoppingagg" | |||||
}, | |||||
"Logging": { | "Logging": { | ||||
"Debug": { | |||||
"IncludeScopes": false, | |||||
"LogLevel": { | |||||
"Default": "Warning" | |||||
} | |||||
"LogLevel": { | |||||
"Default": "Information", | |||||
"Microsoft.AspNetCore": "Warning" | |||||
} | |||||
}, | |||||
"OpenApi": { | |||||
"Endpoint": { | |||||
"Name": "Purchase BFF V1" | |||||
}, | }, | ||||
"Console": { | |||||
"IncludeScopes": false, | |||||
"LogLevel": { | |||||
"Default": "Warning" | |||||
} | |||||
"Document": { | |||||
"Description": "Shopping Aggregator for Web Clients", | |||||
"Title": "Shopping Aggregator for Web Clients", | |||||
"Version": "v1" | |||||
}, | |||||
"Auth": { | |||||
"ClientId": "webshoppingaggswaggerui", | |||||
"AppName": "web shopping bff Swagger UI" | |||||
} | |||||
}, | |||||
"Identity": { | |||||
"Url": "http://localhost:5105", | |||||
"Audience": "webshoppingagg", | |||||
"Scopes": { | |||||
"webshoppingagg": "Shopping Aggregator for Web Clients" | |||||
} | } | ||||
} | } | ||||
} | } |