|
@ -1,9 +1,7 @@ |
|
|
using System; |
|
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
|
using System.IdentityModel.Tokens.Jwt; |
|
|
|
|
|
using System.Net.Http; |
|
|
|
|
|
|
|
|
using HealthChecks.UI.Client; |
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer; |
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer; |
|
|
using Microsoft.AspNetCore.Builder; |
|
|
using Microsoft.AspNetCore.Builder; |
|
|
|
|
|
using Microsoft.AspNetCore.Diagnostics.HealthChecks; |
|
|
using Microsoft.AspNetCore.Hosting; |
|
|
using Microsoft.AspNetCore.Hosting; |
|
|
using Microsoft.AspNetCore.Http; |
|
|
using Microsoft.AspNetCore.Http; |
|
|
using Microsoft.AspNetCore.Mvc; |
|
|
using Microsoft.AspNetCore.Mvc; |
|
@ -13,207 +11,206 @@ using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure; |
|
|
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; |
|
|
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; |
|
|
using Microsoft.Extensions.Configuration; |
|
|
using Microsoft.Extensions.Configuration; |
|
|
using Microsoft.Extensions.DependencyInjection; |
|
|
using Microsoft.Extensions.DependencyInjection; |
|
|
|
|
|
using Microsoft.Extensions.Diagnostics.HealthChecks; |
|
|
using Microsoft.Extensions.Logging; |
|
|
using Microsoft.Extensions.Logging; |
|
|
using Polly; |
|
|
using Polly; |
|
|
using Polly.Extensions.Http; |
|
|
using Polly.Extensions.Http; |
|
|
using Swashbuckle.AspNetCore.Swagger; |
|
|
using Swashbuckle.AspNetCore.Swagger; |
|
|
using HealthChecks.UI.Client; |
|
|
|
|
|
using Microsoft.AspNetCore.Diagnostics.HealthChecks; |
|
|
|
|
|
using Microsoft.Extensions.Diagnostics.HealthChecks; |
|
|
|
|
|
|
|
|
using System; |
|
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
|
using System.IdentityModel.Tokens.Jwt; |
|
|
|
|
|
using System.Net.Http; |
|
|
|
|
|
using System.Threading.Tasks; |
|
|
|
|
|
|
|
|
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator |
|
|
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator |
|
|
{ |
|
|
{ |
|
|
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.AddHealthChecks() |
|
|
|
|
|
.AddCheck("self", () => HealthCheckResult.Healthy()) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" }); |
|
|
|
|
|
|
|
|
|
|
|
services.AddCustomMvc(Configuration) |
|
|
|
|
|
.AddCustomAuthentication(Configuration) |
|
|
|
|
|
.AddHttpServices(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
|
|
|
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) |
|
|
|
|
|
{ |
|
|
|
|
|
var pathBase = Configuration["PATH_BASE"]; |
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(pathBase)) |
|
|
|
|
|
{ |
|
|
|
|
|
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase); |
|
|
|
|
|
app.UsePathBase(pathBase); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
app.UseHealthChecks("/hc", new HealthCheckOptions() |
|
|
|
|
|
{ |
|
|
|
|
|
Predicate = _ => true, |
|
|
|
|
|
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
app.UseHealthChecks("/liveness", new HealthCheckOptions |
|
|
|
|
|
{ |
|
|
|
|
|
Predicate = r => r.Name.Contains("self") |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
app.UseCors("CorsPolicy"); |
|
|
|
|
|
|
|
|
|
|
|
if (env.IsDevelopment()) |
|
|
|
|
|
{ |
|
|
|
|
|
app.UseDeveloperExceptionPage(); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
|
|
|
|
app.UseHsts(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
app.UseAuthentication(); |
|
|
|
|
|
app.UseHttpsRedirection(); |
|
|
|
|
|
app.UseMvc(); |
|
|
|
|
|
|
|
|
|
|
|
app.UseSwagger().UseSwaggerUI(c => |
|
|
|
|
|
{ |
|
|
|
|
|
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); |
|
|
|
|
|
|
|
|
|
|
|
c.OAuthClientId("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui"); |
|
|
|
|
|
c.OAuthClientSecret(string.Empty); |
|
|
|
|
|
c.OAuthRealm(string.Empty); |
|
|
|
|
|
c.OAuthAppName("Purchase BFF Swagger UI"); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static class ServiceCollectionExtensions |
|
|
|
|
|
{ |
|
|
|
|
|
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) |
|
|
|
|
|
{ |
|
|
|
|
|
services.AddOptions(); |
|
|
|
|
|
services.Configure<UrlsConfig>(configuration.GetSection("urls")); |
|
|
|
|
|
|
|
|
|
|
|
services.AddMvc() |
|
|
|
|
|
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2); |
|
|
|
|
|
|
|
|
|
|
|
services.AddSwaggerGen(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.DescribeAllEnumsAsStrings(); |
|
|
|
|
|
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info |
|
|
|
|
|
{ |
|
|
|
|
|
Title = "Shopping Aggregator for Mobile Clients", |
|
|
|
|
|
Version = "v1", |
|
|
|
|
|
Description = "Shopping Aggregator for Mobile Clients", |
|
|
|
|
|
TermsOfService = "Terms Of Service" |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
options.AddSecurityDefinition("oauth2", new OAuth2Scheme |
|
|
|
|
|
{ |
|
|
|
|
|
Type = "oauth2", |
|
|
|
|
|
Flow = "implicit", |
|
|
|
|
|
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", |
|
|
|
|
|
TokenUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", |
|
|
|
|
|
Scopes = new Dictionary<string, string>() |
|
|
|
|
|
{ |
|
|
|
|
|
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" } |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
options.OperationFilter<AuthorizeCheckOperationFilter>(); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
services.AddCors(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.AddPolicy("CorsPolicy", |
|
|
|
|
|
builder => builder |
|
|
|
|
|
.AllowAnyMethod() |
|
|
|
|
|
.AllowAnyHeader() |
|
|
|
|
|
.SetIsOriginAllowed((host) => true) |
|
|
|
|
|
.AllowCredentials()); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return services; |
|
|
|
|
|
} |
|
|
|
|
|
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) |
|
|
|
|
|
{ |
|
|
|
|
|
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); |
|
|
|
|
|
var identityUrl = configuration.GetValue<string>("urls:identity"); |
|
|
|
|
|
|
|
|
|
|
|
services.AddAuthentication(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; |
|
|
|
|
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; |
|
|
|
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
.AddJwtBearer(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.Authority = identityUrl; |
|
|
|
|
|
options.RequireHttpsMetadata = false; |
|
|
|
|
|
options.Audience = "mobileshoppingagg"; |
|
|
|
|
|
options.Events = new JwtBearerEvents() |
|
|
|
|
|
{ |
|
|
|
|
|
OnAuthenticationFailed = async ctx => |
|
|
|
|
|
{ |
|
|
|
|
|
}, |
|
|
|
|
|
OnTokenValidated = async ctx => |
|
|
|
|
|
{ |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return services; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static IServiceCollection AddHttpServices(this IServiceCollection services) |
|
|
|
|
|
{ |
|
|
|
|
|
//register delegating handlers
|
|
|
|
|
|
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); |
|
|
|
|
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); |
|
|
|
|
|
|
|
|
|
|
|
//register http services
|
|
|
|
|
|
services.AddHttpClient<IBasketService, BasketService>() |
|
|
|
|
|
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() |
|
|
|
|
|
.AddPolicyHandler(GetRetryPolicy()) |
|
|
|
|
|
.AddPolicyHandler(GetCircuitBreakerPolicy()); |
|
|
|
|
|
|
|
|
|
|
|
services.AddHttpClient<ICatalogService, CatalogService>() |
|
|
|
|
|
.AddPolicyHandler(GetRetryPolicy()) |
|
|
|
|
|
.AddPolicyHandler(GetCircuitBreakerPolicy()); |
|
|
|
|
|
|
|
|
|
|
|
services.AddHttpClient<IOrderApiClient, OrderApiClient>() |
|
|
|
|
|
.AddPolicyHandler(GetRetryPolicy()) |
|
|
|
|
|
.AddPolicyHandler(GetCircuitBreakerPolicy()); |
|
|
|
|
|
|
|
|
|
|
|
return services; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() |
|
|
|
|
|
{ |
|
|
|
|
|
return HttpPolicyExtensions |
|
|
|
|
|
.HandleTransientHttpError() |
|
|
|
|
|
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) |
|
|
|
|
|
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy() |
|
|
|
|
|
{ |
|
|
|
|
|
return HttpPolicyExtensions |
|
|
|
|
|
.HandleTransientHttpError() |
|
|
|
|
|
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
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.AddHealthChecks() |
|
|
|
|
|
.AddCheck("self", () => HealthCheckResult.Healthy()) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }) |
|
|
|
|
|
.AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" }); |
|
|
|
|
|
|
|
|
|
|
|
services.AddCustomMvc(Configuration) |
|
|
|
|
|
.AddCustomAuthentication(Configuration) |
|
|
|
|
|
.AddHttpServices(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
|
|
|
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) |
|
|
|
|
|
{ |
|
|
|
|
|
var pathBase = Configuration["PATH_BASE"]; |
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(pathBase)) |
|
|
|
|
|
{ |
|
|
|
|
|
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase); |
|
|
|
|
|
app.UsePathBase(pathBase); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
app.UseHealthChecks("/hc", new HealthCheckOptions() |
|
|
|
|
|
{ |
|
|
|
|
|
Predicate = _ => true, |
|
|
|
|
|
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
app.UseHealthChecks("/liveness", new HealthCheckOptions |
|
|
|
|
|
{ |
|
|
|
|
|
Predicate = r => r.Name.Contains("self") |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
app.UseCors("CorsPolicy"); |
|
|
|
|
|
|
|
|
|
|
|
if (env.IsDevelopment()) |
|
|
|
|
|
{ |
|
|
|
|
|
app.UseDeveloperExceptionPage(); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
|
|
|
|
app.UseHsts(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
app.UseAuthentication(); |
|
|
|
|
|
app.UseHttpsRedirection(); |
|
|
|
|
|
app.UseMvc(); |
|
|
|
|
|
|
|
|
|
|
|
app.UseSwagger().UseSwaggerUI(c => |
|
|
|
|
|
{ |
|
|
|
|
|
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); |
|
|
|
|
|
|
|
|
|
|
|
c.OAuthClientId("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui"); |
|
|
|
|
|
c.OAuthClientSecret(string.Empty); |
|
|
|
|
|
c.OAuthRealm(string.Empty); |
|
|
|
|
|
c.OAuthAppName("Purchase BFF Swagger UI"); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static class ServiceCollectionExtensions |
|
|
|
|
|
{ |
|
|
|
|
|
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) |
|
|
|
|
|
{ |
|
|
|
|
|
services.AddOptions(); |
|
|
|
|
|
services.Configure<UrlsConfig>(configuration.GetSection("urls")); |
|
|
|
|
|
|
|
|
|
|
|
services.AddMvc() |
|
|
|
|
|
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2); |
|
|
|
|
|
|
|
|
|
|
|
services.AddSwaggerGen(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.DescribeAllEnumsAsStrings(); |
|
|
|
|
|
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info |
|
|
|
|
|
{ |
|
|
|
|
|
Title = "Shopping Aggregator for Mobile Clients", |
|
|
|
|
|
Version = "v1", |
|
|
|
|
|
Description = "Shopping Aggregator for Mobile Clients", |
|
|
|
|
|
TermsOfService = "Terms Of Service" |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
options.AddSecurityDefinition("oauth2", new OAuth2Scheme |
|
|
|
|
|
{ |
|
|
|
|
|
Type = "oauth2", |
|
|
|
|
|
Flow = "implicit", |
|
|
|
|
|
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", |
|
|
|
|
|
TokenUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", |
|
|
|
|
|
Scopes = new Dictionary<string, string>() |
|
|
|
|
|
{ |
|
|
|
|
|
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" } |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
options.OperationFilter<AuthorizeCheckOperationFilter>(); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
services.AddCors(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.AddPolicy("CorsPolicy", |
|
|
|
|
|
builder => builder |
|
|
|
|
|
.AllowAnyMethod() |
|
|
|
|
|
.AllowAnyHeader() |
|
|
|
|
|
.SetIsOriginAllowed((host) => true) |
|
|
|
|
|
.AllowCredentials()); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return services; |
|
|
|
|
|
} |
|
|
|
|
|
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) |
|
|
|
|
|
{ |
|
|
|
|
|
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); |
|
|
|
|
|
var identityUrl = configuration.GetValue<string>("urls:identity"); |
|
|
|
|
|
|
|
|
|
|
|
services.AddAuthentication(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; |
|
|
|
|
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; |
|
|
|
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
.AddJwtBearer(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.Authority = identityUrl; |
|
|
|
|
|
options.RequireHttpsMetadata = false; |
|
|
|
|
|
options.Audience = "mobileshoppingagg"; |
|
|
|
|
|
options.Events = new JwtBearerEvents() |
|
|
|
|
|
{ |
|
|
|
|
|
OnAuthenticationFailed = async ctx => |
|
|
|
|
|
{ |
|
|
|
|
|
await Task.Run(() => { }); |
|
|
|
|
|
}, |
|
|
|
|
|
OnTokenValidated = async ctx => |
|
|
|
|
|
{ |
|
|
|
|
|
await Task.Run(() => { }); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return services; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static IServiceCollection AddHttpServices(this IServiceCollection services) |
|
|
|
|
|
{ |
|
|
|
|
|
//register delegating handlers
|
|
|
|
|
|
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); |
|
|
|
|
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); |
|
|
|
|
|
|
|
|
|
|
|
//register http services
|
|
|
|
|
|
services.AddHttpClient<IBasketService, BasketService>() |
|
|
|
|
|
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() |
|
|
|
|
|
.AddPolicyHandler(GetRetryPolicy()) |
|
|
|
|
|
.AddPolicyHandler(GetCircuitBreakerPolicy()); |
|
|
|
|
|
|
|
|
|
|
|
services.AddHttpClient<ICatalogService, CatalogService>() |
|
|
|
|
|
.AddPolicyHandler(GetRetryPolicy()) |
|
|
|
|
|
.AddPolicyHandler(GetCircuitBreakerPolicy()); |
|
|
|
|
|
|
|
|
|
|
|
services.AddHttpClient<IOrderApiClient, OrderApiClient>() |
|
|
|
|
|
.AddPolicyHandler(GetRetryPolicy()) |
|
|
|
|
|
.AddPolicyHandler(GetCircuitBreakerPolicy()); |
|
|
|
|
|
|
|
|
|
|
|
return services; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() |
|
|
|
|
|
=> HttpPolicyExtensions |
|
|
|
|
|
.HandleTransientHttpError() |
|
|
|
|
|
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) |
|
|
|
|
|
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); |
|
|
|
|
|
|
|
|
|
|
|
private static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy() |
|
|
|
|
|
=> HttpPolicyExtensions |
|
|
|
|
|
.HandleTransientHttpError() |
|
|
|
|
|
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); |
|
|
|
|
|
} |
|
|
} |
|
|
} |