Merge branch 'migration/net-5' into feature/ordering-api-migration

This commit is contained in:
David Sanz 2020-12-16 13:54:42 +01:00
commit 1e212aa2e8
20 changed files with 364 additions and 314 deletions

View File

@ -0,0 +1,43 @@
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure
{
public class GrpcExceptionInterceptor : Interceptor
{
private readonly ILogger<GrpcExceptionInterceptor> _logger;
public GrpcExceptionInterceptor(ILogger<GrpcExceptionInterceptor> logger)
{
_logger = logger;
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
}
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
{
try
{
var response = await t;
_logger.LogDebug($"Response received: {response}");
return response;
}
catch (RpcException e)
{
_logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message);
return default;
}
}
}
}

View File

@ -20,6 +20,7 @@
<PackageReference Include="Google.Protobuf" Version="3.11.2" /> <PackageReference Include="Google.Protobuf" Version="3.11.2" />
<PackageReference Include="Grpc.AspNetCore.Server.ClientFactory" Version="2.25.0" /> <PackageReference Include="Grpc.AspNetCore.Server.ClientFactory" Version="2.25.0" />
<PackageReference Include="Grpc.Core" Version="2.25.0" /> <PackageReference Include="Grpc.Core" Version="2.25.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.25.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0" PrivateAssets="All" /> <PackageReference Include="Grpc.Tools" Version="2.25.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />

View File

@ -1,55 +1,29 @@
using Microsoft.AspNetCore; using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator;
using Serilog; using Serilog;
using System.IO;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
public class Program
{
private static IConfiguration _configuration;
public static void Main(string[] args) BuildWebHost(args).Run();
IWebHost BuildWebHost(string[] args) =>
WebHost
.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(cb =>
{ {
_configuration = GetConfiguration(); var sources = cb.Sources;
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource()
BuildWebHost(args).Run(); {
} Optional = true,
Path = "appsettings.localhost.json",
public static IWebHost BuildWebHost(string[] args) => ReloadOnChange = false
WebHost });
.CreateDefaultBuilder(args) })
.ConfigureAppConfiguration(cb => .UseStartup<Startup>()
{ .UseSerilog((builderContext, config) =>
var sources = cb.Sources;
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource()
{
Optional = true,
Path = "appsettings.localhost.json",
ReloadOnChange = false
});
})
.UseStartup<Startup>()
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.Build();
private static IConfiguration GetConfiguration()
{ {
var builder = new ConfigurationBuilder() config
.SetBasePath(Directory.GetCurrentDirectory()) .MinimumLevel.Information()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Enrich.FromLogContext()
.AddEnvironmentVariables(); .WriteTo.Console();
})
var config = builder.Build(); .Build();
return builder.Build();
}
}
}

View File

@ -1,54 +1,40 @@
using Grpc.Net.Client; using GrpcBasket;
using GrpcBasket;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
public class BasketService : IBasketService public class BasketService : IBasketService
{ {
private readonly HttpClient _httpClient; private readonly Basket.BasketClient _basketClient;
private readonly UrlsConfig _urls;
private readonly ILogger<BasketService> _logger; private readonly ILogger<BasketService> _logger;
public BasketService(HttpClient httpClient, IOptions<UrlsConfig> config, ILogger<BasketService> logger) public BasketService(Basket.BasketClient basketClient, ILogger<BasketService> logger)
{ {
_httpClient = httpClient; _basketClient = basketClient;
_urls = config.Value;
_logger = logger; _logger = logger;
} }
public async Task<BasketData> GetById(string id) public async Task<BasketData> GetById(string id)
{ {
return await GrpcCallerService.CallService(_urls.GrpcBasket, async channel => _logger.LogDebug("grpc client created, request = {@id}", id);
{ var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id });
_logger.LogDebug("grpc response {@response}", response);
var client = new Basket.BasketClient(channel);
_logger.LogDebug("grpc client created, request = {@id}", id);
var response = await client.GetBasketByIdAsync(new BasketRequest { Id = id });
_logger.LogDebug("grpc response {@response}", response);
return MapToBasketData(response); return MapToBasketData(response);
});
} }
public async Task UpdateAsync(BasketData currentBasket) public async Task UpdateAsync(BasketData currentBasket)
{ {
await GrpcCallerService.CallService(_urls.GrpcBasket, async httpClient => _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
{ var request = MapToCustomerBasketRequest(currentBasket);
var channel = GrpcChannel.ForAddress(_urls.GrpcBasket); _logger.LogDebug("Grpc update basket request {@request}", request);
var client = new Basket.BasketClient(channel);
_logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
var request = MapToCustomerBasketRequest(currentBasket);
_logger.LogDebug("Grpc update basket request {@request}", request);
return await client.UpdateBasketAsync(request); await _basketClient.UpdateBasketAsync(request);
});
} }
private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest) private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest)

View File

@ -1,4 +1,5 @@
using Devspaces.Support; using Devspaces.Support;
using GrpcBasket;
using HealthChecks.UI.Client; using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
@ -14,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -46,7 +48,8 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
services.AddCustomMvc(Configuration) services.AddCustomMvc(Configuration)
.AddCustomAuthentication(Configuration) .AddCustomAuthentication(Configuration)
.AddDevspaces() .AddDevspaces()
.AddHttpServices(); .AddHttpServices()
.AddGrpcServices();
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -177,10 +180,6 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//register http services //register http services
services
.AddHttpClient<IBasketService, BasketService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddDevspacesSupport();
services.AddHttpClient<ICatalogService, CatalogService>() services.AddHttpClient<ICatalogService, CatalogService>()
.AddDevspacesSupport(); .AddDevspacesSupport();
@ -194,5 +193,20 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
return services; return services;
} }
public static IServiceCollection AddGrpcServices(this IServiceCollection services)
{
services.AddSingleton<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>();
return services;
}
} }
} }

View File

@ -1,8 +1,8 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src WORKDIR /src
# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles # It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles

View File

@ -0,0 +1,43 @@
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure
{
public class GrpcExceptionInterceptor : Interceptor
{
private readonly ILogger<GrpcExceptionInterceptor> _logger;
public GrpcExceptionInterceptor(ILogger<GrpcExceptionInterceptor> logger)
{
_logger = logger;
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
}
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
{
try
{
var response = await t;
_logger.LogDebug($"Response received: {response}");
return response;
}
catch (RpcException e)
{
_logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message);
return default;
}
}
}
}

View File

@ -1,38 +1,29 @@
using Microsoft.AspNetCore; using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator;
using Serilog; using Serilog;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator BuildWebHost(args).Run();
{
public class Program IWebHost BuildWebHost(string[] args) =>
{ WebHost
public static void Main(string[] args) .CreateDefaultBuilder(args)
.ConfigureAppConfiguration(cb =>
{ {
BuildWebHost(args).Run(); var sources = cb.Sources;
} sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource()
{
public static IWebHost BuildWebHost(string[] args) => Optional = true,
WebHost Path = "appsettings.localhost.json",
.CreateDefaultBuilder(args) ReloadOnChange = false
.ConfigureAppConfiguration(cb => });
{ })
var sources = cb.Sources; .UseStartup<Startup>()
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource() .UseSerilog((builderContext, config) =>
{ {
Optional = true, config
Path = "appsettings.localhost.json", .MinimumLevel.Information()
ReloadOnChange = false .Enrich.FromLogContext()
}); .WriteTo.Console();
}) })
.UseStartup<Startup>() .Build();
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.Build();
}
}

View File

@ -4,49 +4,38 @@ using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{ {
public class BasketService : IBasketService public class BasketService : IBasketService
{ {
private readonly UrlsConfig _urls; private readonly Basket.BasketClient _basketClient;
public readonly HttpClient _httpClient;
private readonly ILogger<BasketService> _logger; private readonly ILogger<BasketService> _logger;
public BasketService(HttpClient httpClient, IOptions<UrlsConfig> config, ILogger<BasketService> logger) public BasketService(Basket.BasketClient basketClient, ILogger<BasketService> logger)
{ {
_urls = config.Value; _basketClient = basketClient;
_httpClient = httpClient;
_logger = logger; _logger = logger;
} }
public async Task<BasketData> GetById(string id) public async Task<BasketData> GetById(string id)
{ {
return await GrpcCallerService.CallService(_urls.GrpcBasket, async channel => _logger.LogDebug("grpc client created, request = {@id}", id);
{ var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id });
var client = new Basket.BasketClient(channel); _logger.LogDebug("grpc response {@response}", response);
_logger.LogDebug("grpc client created, request = {@id}", id);
var response = await client.GetBasketByIdAsync(new BasketRequest { Id = id });
_logger.LogDebug("grpc response {@response}", response);
return MapToBasketData(response); return MapToBasketData(response);
});
} }
public async Task UpdateAsync(BasketData currentBasket) public async Task UpdateAsync(BasketData currentBasket)
{ {
await GrpcCallerService.CallService(_urls.GrpcBasket, async channel => _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
{ var request = MapToCustomerBasketRequest(currentBasket);
var client = new Basket.BasketClient(channel); _logger.LogDebug("Grpc update basket request {@request}", request);
_logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
var request = MapToCustomerBasketRequest(currentBasket);
_logger.LogDebug("Grpc update basket request {@request}", request);
return await client.UpdateBasketAsync(request); await _basketClient.UpdateBasketAsync(request);
});
} }
private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest) private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest)

View File

@ -1,4 +1,5 @@
using Devspaces.Support; using Devspaces.Support;
using GrpcBasket;
using HealthChecks.UI.Client; using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
@ -14,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -42,11 +44,12 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
.AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" }) .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["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" })
.AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" }); .AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" });
services.AddCustomMvc(Configuration) services.AddCustomMvc(Configuration)
.AddCustomAuthentication(Configuration) .AddCustomAuthentication(Configuration)
.AddDevspaces() .AddDevspaces()
.AddApplicationServices(); .AddApplicationServices()
.AddGrpcServices();
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -63,7 +66,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseSwagger().UseSwaggerUI(c => app.UseSwagger().UseSwaggerUI(c =>
@ -139,7 +142,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
Version = "v1", Version = "v1",
Description = "Shopping Aggregator for Web Clients" Description = "Shopping Aggregator for Web Clients"
}); });
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{ {
Type = SecuritySchemeType.OAuth2, Type = SecuritySchemeType.OAuth2,
@ -181,10 +184,6 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
//register http services //register http services
services.AddHttpClient<IBasketService, BasketService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddDevspacesSupport();
services.AddHttpClient<ICatalogService, CatalogService>() services.AddHttpClient<ICatalogService, CatalogService>()
.AddDevspacesSupport(); .AddDevspacesSupport();
@ -199,6 +198,19 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
return services; return services;
} }
public static IServiceCollection AddGrpcServices(this IServiceCollection services)
{
services.AddSingleton<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>();
return services;
}
} }
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<AssemblyName>Web.Shopping.HttpAggregator</AssemblyName> <AssemblyName>Web.Shopping.HttpAggregator</AssemblyName>
<RootNamespace>Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator</RootNamespace> <RootNamespace>Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator</RootNamespace>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
@ -15,20 +15,21 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="3.0.0" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="3.1.2" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="3.0.0" /> <PackageReference Include="AspNetCore.HealthChecks.Uris" Version="3.1.2" />
<PackageReference Include="Google.Protobuf" Version="3.11.2" /> <PackageReference Include="Google.Protobuf" Version="3.14.0" />
<PackageReference Include="Grpc.AspNetCore.Server.ClientFactory" Version="2.25.0" /> <PackageReference Include="Grpc.AspNetCore.Server.ClientFactory" Version="2.34.0" />
<PackageReference Include="Grpc.Core" Version="2.25.0" /> <PackageReference Include="Grpc.Core" Version="2.34.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.25.0" /> <PackageReference Include="Grpc.Net.Client" Version="2.34.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0" PrivateAssets="All" /> <PackageReference Include="Grpc.Net.ClientFactory" Version="2.34.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" /> <PackageReference Include="Grpc.Tools" Version="2.34.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.1" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.0" /> <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" /> <PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc5" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<!--<PackageReference Include="System.Net.Http.WinHttpHandler" Version="4.6.0-rc1.19456.4" />--> <!--<PackageReference Include="System.Net.Http.WinHttpHandler" Version="4.6.0-rc1.19456.4" />-->
</ItemGroup> </ItemGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks> <GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
@ -16,30 +16,30 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="3.0.0" /> <PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="3.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="3.0.3" /> <PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="3.1.4" />
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="3.0.0" /> <PackageReference Include="AspNetCore.HealthChecks.Redis" Version="3.1.2" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="3.0.0" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="3.1.2" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="5.0.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.1.0" />
<PackageReference Include="Google.Protobuf" Version="3.11.2" /> <PackageReference Include="Google.Protobuf" Version="3.14.0" />
<PackageReference Include="Grpc.AspNetCore.Server" Version="2.25.0" /> <PackageReference Include="Grpc.AspNetCore.Server" Version="2.34.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0" PrivateAssets="All" /> <PackageReference Include="Grpc.Tools" Version="2.34.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.12.0" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.16.0" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.12.0" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.16.0" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.1.1" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" /> <PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.10" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="5.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" /> <PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" /> <PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.1-dev-00216" /> <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.1-dev-00216" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" />
<PackageReference Include="Serilog.Sinks.Http" Version="5.2.0" /> <PackageReference Include="Serilog.Sinks.Http" Version="7.2.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.1.0-dev-00166" /> <PackageReference Include="Serilog.Sinks.Seq" Version="4.1.0-dev-00166" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc5" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,8 +1,8 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src WORKDIR /src
# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles # It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles

View File

@ -2,29 +2,29 @@
namespace Basket.API.Model namespace Basket.API.Model
{ {
public class BasketCheckout public record BasketCheckout
{ {
public string City { get; set; } public string City { get; init; }
public string Street { get; set; } public string Street { get; init; }
public string State { get; set; } public string State { get; init; }
public string Country { get; set; } public string Country { get; init; }
public string ZipCode { get; set; } public string ZipCode { get; init; }
public string CardNumber { get; set; } public string CardNumber { get; init; }
public string CardHolderName { get; set; } public string CardHolderName { get; init; }
public DateTime CardExpiration { get; set; } public DateTime CardExpiration { get; init; }
public string CardSecurityNumber { get; set; } public string CardSecurityNumber { get; init; }
public int CardTypeId { get; set; } public int CardTypeId { get; init; }
public string Buyer { get; set; } public string Buyer { get; init; }
public Guid RequestId { get; set; } public Guid RequestId { get; set; }
} }

View File

@ -3,15 +3,15 @@ using System.ComponentModel.DataAnnotations;
namespace Microsoft.eShopOnContainers.Services.Basket.API.Model namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
{ {
public class BasketItem : IValidatableObject public record BasketItem : IValidatableObject
{ {
public string Id { get; set; } public string Id { get; init; }
public int ProductId { get; set; } public int ProductId { get; init; }
public string ProductName { get; set; } public string ProductName { get; init; }
public decimal UnitPrice { get; set; } public decimal UnitPrice { get; set; }
public decimal OldUnitPrice { get; set; } public decimal OldUnitPrice { get; set; }
public int Quantity { get; set; } public int Quantity { get; init; }
public string PictureUrl { get; set; } public string PictureUrl { get; init; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{ {
var results = new List<ValidationResult>(); var results = new List<ValidationResult>();

View File

@ -2,9 +2,9 @@
namespace Microsoft.eShopOnContainers.Services.Basket.API.Model namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
{ {
public class CustomerBasket public record CustomerBasket
{ {
public string BuyerId { get; set; } public string BuyerId { get; init; }
public List<BasketItem> Items { get; set; } = new List<BasketItem>(); public List<BasketItem> Items { get; set; } = new List<BasketItem>();

View File

@ -3,113 +3,109 @@ using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.eShopOnContainers.Services.Basket.API;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Serilog; using Serilog;
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
namespace Microsoft.eShopOnContainers.Services.Basket.API var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration);
try
{ {
public class Program Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName);
{ var host = BuildWebHost(configuration, args);
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
public static int Main(string[] args) Log.Information("Starting web host ({ApplicationContext})...", Program.AppName);
{ host.Run();
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration); return 0;
try
{
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(false)
.ConfigureKestrel(options =>
{
var ports = GetDefinedPorts(configuration);
options.Listen(IPAddress.Any, ports.httpPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
options.Listen(IPAddress.Any, ports.grpcPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
})
.ConfigureAppConfiguration(x => x.AddConfiguration(configuration))
.UseFailing(options => {
options.ConfigPath = "/Failing";
options.NotFilteredPaths.AddRange(new[] {"/hc","/liveness"});
})
.UseStartup<Startup>()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseSerilog()
.Build();
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
var logstashUrl = configuration["Serilog:LogstashgUrl"];
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
var config = builder.Build();
if (config.GetValue<bool>("UseVault", false))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
private static (int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config)
{
var grpcPort = config.GetValue("GRPC_PORT", 5001);
var port = config.GetValue("PORT", 80);
return (port, grpcPort);
}
}
} }
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", Program.AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(false)
.ConfigureKestrel(options =>
{
var ports = GetDefinedPorts(configuration);
options.Listen(IPAddress.Any, ports.httpPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
options.Listen(IPAddress.Any, ports.grpcPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
})
.ConfigureAppConfiguration(x => x.AddConfiguration(configuration))
.UseFailing(options =>
{
options.ConfigPath = "/Failing";
options.NotFilteredPaths.AddRange(new[] { "/hc", "/liveness" });
})
.UseStartup<Startup>()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseSerilog()
.Build();
ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
var logstashUrl = configuration["Serilog:LogstashgUrl"];
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", Program.AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
var config = builder.Build();
if (config.GetValue<bool>("UseVault", false))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config)
{
var grpcPort = config.GetValue("GRPC_PORT", 5001);
var port = config.GetValue("PORT", 80);
return (port, grpcPort);
}
public class Program {
public static string Namespace = typeof(Startup).Namespace;
public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
@ -16,11 +16,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.0" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Moq" Version="4.13.1" /> <PackageReference Include="Moq" Version="4.15.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks> <GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MediatR" Version="7.0.0" /> <PackageReference Include="MediatR" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.1" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="5.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Microsoft.NETCore.Platforms" Version="3.1.0" /> <PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.0" />
<PackageReference Include="Moq" Version="4.13.1" /> <PackageReference Include="Moq" Version="4.15.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -67,10 +67,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.0" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.1" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>