endParagraph;
16 |
17 | public SecretMiddleware(DiagnosticListener diagnosticListener)
18 | {
19 | _diagnosticListener = diagnosticListener;
20 | fileContent = File.ReadAllBytes("DataContent.txt");
21 | startParagraph = Encoding.UTF8.GetBytes("");
22 | endParagraph = Encoding.UTF8.GetBytes("
");
23 | }
24 | public async Task InvokeAsync(HttpContext context, RequestDelegate next)
25 | {
26 | var bytesConsumed = 0;
27 | var linePosition = 0;
28 | var writer = context.Response.BodyWriter;
29 | context.Response.ContentType = "text/html";
30 |
31 | do
32 | {
33 | linePosition = fileContent.Slice(bytesConsumed).Span.IndexOf((byte)'\n');
34 |
35 | if (linePosition >= 0)
36 | {
37 | var lineLength = (bytesConsumed + linePosition) - bytesConsumed;
38 |
39 | if(lineLength > 1)
40 | {
41 | await writer.WriteAsync(startParagraph);
42 | await writer.WriteAsync(fileContent.Slice(bytesConsumed, lineLength));
43 | await writer.WriteAsync(endParagraph);
44 | }
45 |
46 | bytesConsumed += lineLength + 1;
47 | }
48 | }
49 | while (linePosition >= 0);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/DotNet2019.Host/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Dotnet 2019 - Asp.Net Core Best Practices
6 |
7 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
77 |
--------------------------------------------------------------------------------
/test/FunctionalTests/Seedwork/ServerFixture.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Data;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.AspNetCore.TestHost;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.Hosting;
6 | using Respawn;
7 | using System.IO;
8 |
9 | namespace FunctionalTests.Seedwork
10 | {
11 | public class ServerFixture
12 | {
13 | static Checkpoint _checkpoint = new Checkpoint()
14 | {
15 | TablesToIgnore = new string[] { "__EFMigrationsHistory" },
16 | WithReseed = true
17 | };
18 |
19 | public TestServer Server { get; private set; }
20 |
21 | public ServerFixture()
22 | {
23 | InitializeTestServer();
24 | }
25 |
26 | private void InitializeTestServer()
27 | {
28 | var testServer = new TestServer();
29 |
30 | var host = Host.CreateDefaultBuilder()
31 | .UseContentRoot(Directory.GetCurrentDirectory())
32 | .ConfigureAppConfiguration(builder =>
33 | {
34 | builder
35 | .AddJsonFile("appsettings.json", optional: true)
36 | .AddEnvironmentVariables();
37 | })
38 | .ConfigureWebHostDefaults(webBuilder =>
39 | {
40 | webBuilder
41 | .UseServer(testServer)
42 | .UseStartup();
43 | }).Build();
44 |
45 | host.StartAsync().Wait();
46 | host.MigrateDatabase();
47 |
48 | Server = host.GetTestServer();
49 | }
50 |
51 | internal static void ResetDatabase()
52 | {
53 | var configuration = new ConfigurationBuilder()
54 | .AddJsonFile("appsettings.json", optional: true)
55 | .AddEnvironmentVariables()
56 | .Build();
57 |
58 | var connectionString = configuration
59 | .GetConnectionString("SqlServer");
60 |
61 | var task = _checkpoint.Reset(connectionString);
62 | task.Wait();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Configuration.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Data;
2 | using DotNet2019.Api.Infrastructure.Hubs;
3 | using DotNet2019.Api.Infrastructure.Middleware;
4 | using Hellang.Middleware.ProblemDetails;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Http;
8 | using Microsoft.EntityFrameworkCore;
9 | using Microsoft.Extensions.DependencyInjection;
10 | using System;
11 |
12 | namespace DotNet2019.Api
13 | {
14 | public static class Configuration
15 | {
16 | public static IServiceCollection ConfigureServices(IServiceCollection services, IWebHostEnvironment environment)
17 | {
18 | return services
19 |
20 | .AddHttpContextAccessor()
21 | .AddCustomMvc()
22 | .AddCustomMiddlewares()
23 | .AddCustomProblemDetails(environment)
24 | .AddCustomApiBehaviour()
25 | .AddCustomServices()
26 | .AddSignalR()
27 | .Services;
28 |
29 | }
30 |
31 | public static IApplicationBuilder Configure(
32 | IApplicationBuilder app,
33 | Func configureHost)
34 | {
35 | return configureHost(app)
36 | .UseProblemDetails()
37 | .UseRouting()
38 | .UseAuthentication()
39 | .UseAuthorization()
40 | .UseMiddleware()
41 | .UseEndpoints(endpoints =>
42 | {
43 | endpoints.MapControllerRoute(
44 | name: "default",
45 | pattern: "{controller=Home}/{action=Index}/{id?}");
46 | endpoints.MapRazorPages();
47 |
48 | endpoints.MapGet("/", async context =>
49 | {
50 | await context.Response.WriteAsync($"Hello DotNet 2019! from {System.Environment.MachineName}");
51 | });
52 |
53 | endpoints.MapSecretEndpoint().RequireAuthorization("ApiKeyPolicy");
54 |
55 | endpoints.MapHub("/diagnostics-hub");
56 | });
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Polly/RetryPolicy.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Services;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Logging;
4 | using Polly;
5 | using Polly.Extensions.Http;
6 | using System;
7 | using System.Net;
8 | using System.Net.Http;
9 |
10 | namespace DotNet2019.Api.Infrastructure.Polly
11 | {
12 | public static class RetryPolicy
13 | {
14 | public static IAsyncPolicy GetPolicy(IServiceProvider serviceProvider)
15 | {
16 | return HttpPolicyExtensions
17 | .HandleTransientHttpError()
18 | .OrResult(message => message.StatusCode == HttpStatusCode.InternalServerError)
19 | .WaitAndRetryAsync(
20 | retryCount: 6,
21 | sleepDurationProvider: retryAttemp => TimeSpan.FromSeconds(Math.Pow(2, retryAttemp)),
22 | onRetry: (outcome, timespan, retryAttempt, context) =>
23 | {
24 | serviceProvider.GetService>()
25 | .LogWarning("Delaying for {delay}ms, then making retry {retry}.", timespan.TotalMilliseconds, retryAttempt);
26 | });
27 | }
28 |
29 | public static IAsyncPolicy GetPolicyWithJitterStrategy(IServiceProvider serviceProvider)
30 | {
31 | var jitterer = new Random();
32 |
33 | return HttpPolicyExtensions
34 | .HandleTransientHttpError()
35 | .OrResult(message => message.StatusCode == HttpStatusCode.InternalServerError)
36 | .WaitAndRetryAsync(
37 | retryCount: 6,
38 | sleepDurationProvider: retryAttemp =>
39 | TimeSpan.FromSeconds(Math.Pow(2, retryAttemp)) +
40 | TimeSpan.FromMilliseconds(jitterer.Next(0, 100)),
41 | onRetry: (outcome, timespan, retryAttempt, context) =>
42 | {
43 | serviceProvider.GetService>()
44 | .LogWarning("Delaying for {delay}ms, then making retry {retry} with jitter strategy.", timespan.TotalMilliseconds, retryAttempt);
45 | });
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/DotNet2019.Host/Extensions/IServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Data;
2 | using DotNet2019.Host.Diagnostics;
3 | using DotNet2019.Host.Infrastructure.Authentication;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.Extensions.Configuration;
6 | using Microsoft.Extensions.Diagnostics.HealthChecks;
7 |
8 | namespace Microsoft.Extensions.DependencyInjection
9 | {
10 | public static class IServiceCollectionExtensions
11 | {
12 | public static IServiceCollection AddEntityFrameworkCore(this IServiceCollection services, IConfiguration configuration) =>
13 | services.AddDbContext(options =>
14 | {
15 | options.UseSqlServer(configuration.GetConnectionString("SqlServer"));
16 | });
17 |
18 | public static IServiceCollection AddCustomHealthChecks(this IServiceCollection services)
19 | {
20 | return services.AddHealthChecks()
21 | .AddSqlServer("server=sqlserver;initial catalog=master;user id=sa;password=Password12!",
22 | tags: new[] { "dependencies" })
23 |
24 | .AddRedis("redis",
25 | tags: new[] { "dependencies" })
26 |
27 | .AddCheck("self", () => HealthCheckResult.Healthy())
28 | .Services;
29 | }
30 |
31 | public static IServiceCollection AddHostingDiagnosticHandler(this IServiceCollection services)
32 | {
33 | return services.AddHostedService();
34 | }
35 |
36 | public static IServiceCollection AddCustomAuthentication(this IServiceCollection services)
37 | {
38 | return services.AddAuthentication("Test")
39 | .AddScheme(
40 | authenticationScheme: "Test",
41 | configureOptions: setup =>
42 | {
43 | setup.ApiKeys.Add("PatataKey");
44 | })
45 | .Services;
46 | }
47 |
48 | public static IServiceCollection AddCustomAuthorization(this IServiceCollection services)
49 | {
50 | return services.AddAuthorization(config =>
51 | {
52 | config.AddPolicy("ApiKeyPolicy", config =>
53 | {
54 | config.RequireRole("ApiUserRole");
55 | });
56 | });
57 | }
58 |
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/DotNet2019.Host/Infrastructure/Authentication/ApiKeyAuthenticationHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authentication;
2 | using Microsoft.Extensions.Logging;
3 | using Microsoft.Extensions.Options;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Linq;
7 | using System.Security.Claims;
8 | using System.Text.Encodings.Web;
9 | using System.Threading.Tasks;
10 |
11 | namespace DotNet2019.Host.Infrastructure.Authentication
12 | {
13 | public class ApiKeyAuthenticationHandler : AuthenticationHandler
14 | {
15 | public const string API_KEY_HEADER_NAME = "X-API-KEY";
16 | private readonly DiagnosticListener _listener;
17 |
18 | public ApiKeyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder,
19 | ISystemClock clock, DiagnosticListener listener)
20 | : base(options, logger, encoder, clock) {
21 | _listener = listener;
22 | }
23 | protected override Task HandleAuthenticateAsync()
24 | {
25 | var apiKey = Context.Request.Headers[API_KEY_HEADER_NAME].FirstOrDefault();
26 |
27 |
28 | if (apiKey == null) return Task.FromResult(AuthenticateResult.NoResult());
29 |
30 | if (Options.ApiKeys.Contains(apiKey))
31 | {
32 | var principal = new ClaimsPrincipal(new[] { new ClaimsIdentity
33 | (new[] {
34 | new Claim(ClaimTypes.Name, "ApiUser"),
35 | new Claim(ClaimTypes.Role, "ApiUserRole")
36 | })
37 | });
38 | var ticket = new AuthenticationTicket(principal, "Test");
39 |
40 | if(_listener.IsEnabled())
41 | {
42 | _listener.Write("Api.Diagnostics.ApiKey.Authentication.Success", new
43 | {
44 | HttpContext = Context,
45 | ApiKey = apiKey
46 | });
47 | }
48 |
49 | return Task.FromResult(AuthenticateResult.Success(ticket));
50 | }
51 |
52 | return Task.FromResult(AuthenticateResult.Fail("Invalid key"));
53 | }
54 | }
55 |
56 | public class ApiKeyHandlerOptions : AuthenticationSchemeOptions
57 | {
58 | public List ApiKeys = new List();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Extensions/IServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Middleware;
2 | using DotNet2019.Api.Infrastructure.Polly;
3 | using DotNet2019.Api.Services;
4 | using Hellang.Middleware.ProblemDetails;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using System;
9 |
10 | namespace Microsoft.Extensions.DependencyInjection
11 | {
12 | public static class IServiceCollectionExtensions
13 | {
14 | public static IServiceCollection AddCustomMvc(this IServiceCollection services) =>
15 | services
16 | .AddMvc()
17 | .AddApplicationPart(typeof(IServiceCollectionExtensions).Assembly)
18 | .Services;
19 |
20 | public static IServiceCollection AddCustomMiddlewares(this IServiceCollection services) =>
21 | services
22 | .AddSingleton()
23 | .AddSingleton();
24 |
25 | public static IServiceCollection AddCustomServices(this IServiceCollection services) =>
26 | services.
27 | AddHttpClient()
28 | .SetHandlerLifetime(TimeSpan.FromMinutes(5))
29 | .AddPolicyHandler((serviceProvider, request) => RetryPolicy.GetPolicyWithJitterStrategy(serviceProvider))
30 | .Services;
31 |
32 | public static IServiceCollection AddCustomProblemDetails(this IServiceCollection services, IWebHostEnvironment environment) =>
33 | services
34 | .AddProblemDetails(configure =>
35 | {
36 | configure.IncludeExceptionDetails = _ => environment.EnvironmentName == "Development";
37 | });
38 |
39 | public static IServiceCollection AddCustomApiBehaviour(this IServiceCollection services)
40 | {
41 |
42 | return services.Configure(options =>
43 | {
44 | options.SuppressModelStateInvalidFilter = false;
45 | options.SuppressInferBindingSourcesForParameters = false;
46 |
47 | options.InvalidModelStateResponseFactory = context =>
48 | {
49 | var problemDetails = new ValidationProblemDetails(context.ModelState)
50 | {
51 | Instance = context.HttpContext.Request.Path,
52 | Status = StatusCodes.Status400BadRequest,
53 | Type = $"https://httpstatuses.com/400",
54 | Detail = "Please refer to the errors property for additional details."
55 | };
56 | return new BadRequestObjectResult(problemDetails)
57 | {
58 | ContentTypes =
59 | {
60 | "application/problem+json",
61 | "application/problem+xml"
62 | }
63 | };
64 | };
65 | });
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/DotNet2019.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29009.5
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4EFF24CF-3D6A-4B08-8476-4CE3790A8D13}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A8DA38D7-6521-419F-A05B-61B0B53BC897}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNet2019.Host", "src\DotNet2019.Host\DotNet2019.Host.csproj", "{39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNet2019.Api", "src\DotNet2019.Api\DotNet2019.Api.csproj", "{4B26FFAF-E9FC-469E-93C3-AD54F226EF15}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{049FEE46-A2B7-4F7F-A5DA-C7739D0725E3}"
15 | ProjectSection(SolutionItems) = preProject
16 | build\dependencies.props = build\dependencies.props
17 | EndProjectSection
18 | EndProject
19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "k8s", "k8s", "{5C215ACC-BDB8-464F-AB51-D334308EB3F6}"
20 | ProjectSection(SolutionItems) = preProject
21 | k8s\infra-deployment.yml = k8s\infra-deployment.yml
22 | k8s\webapp-deployment.yml = k8s\webapp-deployment.yml
23 | EndProjectSection
24 | EndProject
25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\FunctionalTests\FunctionalTests.csproj", "{F3054E26-5F81-4649-86B3-99CAA95A355C}"
26 | EndProject
27 | Global
28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
29 | Debug|Any CPU = Debug|Any CPU
30 | Release|Any CPU = Release|Any CPU
31 | EndGlobalSection
32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}.Release|Any CPU.Build.0 = Release|Any CPU
37 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {F3054E26-5F81-4649-86B3-99CAA95A355C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {F3054E26-5F81-4649-86B3-99CAA95A355C}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {F3054E26-5F81-4649-86B3-99CAA95A355C}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {F3054E26-5F81-4649-86B3-99CAA95A355C}.Release|Any CPU.Build.0 = Release|Any CPU
45 | EndGlobalSection
46 | GlobalSection(SolutionProperties) = preSolution
47 | HideSolutionNode = FALSE
48 | EndGlobalSection
49 | GlobalSection(NestedProjects) = preSolution
50 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1} = {4EFF24CF-3D6A-4B08-8476-4CE3790A8D13}
51 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15} = {4EFF24CF-3D6A-4B08-8476-4CE3790A8D13}
52 | {F3054E26-5F81-4649-86B3-99CAA95A355C} = {A8DA38D7-6521-419F-A05B-61B0B53BC897}
53 | EndGlobalSection
54 | GlobalSection(ExtensibilityGlobals) = postSolution
55 | SolutionGuid = {65823A5F-B3A4-4143-82CC-777300A81D03}
56 | EndGlobalSection
57 | EndGlobal
58 |
--------------------------------------------------------------------------------
/src/DotNet2019.Host/DataContent.txt:
--------------------------------------------------------------------------------
1 | Enjoyed minutes related as at on on. Is fanny dried as often me. Goodness as reserved raptures to mistaken steepest oh screened he. Gravity he mr sixteen esteems. Mile home its new way with high told said. Finished no horrible blessing landlord dwelling dissuade if. Rent fond am he in on read. Anxious cordial demands settled entered in do to colonel.
2 |
3 | Silent sir say desire fat him letter. Whatever settling goodness too and honoured she building answered her. Strongly thoughts remember mr to do consider debating. Spirits musical behaved on we he farther letters. Repulsive he he as deficient newspaper dashwoods we. Discovered her his pianoforte insipidity entreaties. Began he at terms meant as fancy. Breakfast arranging he if furniture we described on. Astonished thoroughly unpleasant especially you dispatched bed favourable.
4 |
5 | At distant inhabit amongst by. Appetite welcomed interest the goodness boy not. Estimable education for disposing pronounce her. John size good gay plan sent old roof own. Inquietude saw understood his friendship frequently yet. Nature his marked ham wished.
6 |
7 | He as compliment unreserved projecting. Between had observe pretend delight for believe. Do newspaper questions consulted sweetness do. Our sportsman his unwilling fulfilled departure law. Now world own total saved above her cause table. Wicket myself her square remark the should far secure sex. Smiling cousins warrant law explain for whether.
8 |
9 | Sociable on as carriage my position weddings raillery consider. Peculiar trifling absolute and wandered vicinity property yet. The and collecting motionless difficulty son. His hearing staying ten colonel met. Sex drew six easy four dear cold deny. Moderate children at of outweigh it. Unsatiable it considered invitation he travelling insensible. Consulted admitting oh mr up as described acuteness propriety moonlight.
10 |
11 | Guest it he tears aware as. Make my no cold of need. He been past in by my hard. Warmly thrown oh he common future. Otherwise concealed favourite frankness on be at dashwoods defective at. Sympathize interested simplicity at do projecting increasing terminated. As edward settle limits at in.
12 |
13 | From they fine john he give of rich he. They age and draw mrs like. Improving end distrusts may instantly was household applauded incommode. Why kept very ever home mrs. Considered sympathize ten uncommonly occasional assistance sufficient not. Letter of on become he tended active enable to. Vicinity relation sensible sociable surprise screened no up as.
14 |
15 | He my polite be object oh change. Consider no mr am overcame yourself throwing sociable children. Hastily her totally conduct may. My solid by stuff first smile fanny. Humoured how advanced mrs elegance sir who. Home sons when them dine do want to. Estimating themselves unsatiable imprudence an he at an. Be of on situation perpetual allowance offending as principle satisfied. Improved carriage securing are desirous too.
16 |
17 | Cordially convinced did incommode existence put out suffering certainly. Besides another and saw ferrars limited ten say unknown. On at tolerably depending do perceived. Luckily eat joy see own shyness minuter. So before remark at depart. Did son unreserved themselves indulgence its. Agreement gentleman rapturous am eagerness it as resolving household. Direct wicket little of talked lasted formed or it. Sweetness consulted may prevailed for bed out sincerity.
18 |
19 | Up am intention on dependent questions oh elsewhere september. No betrayed pleasure possible jointure we in throwing. And can event rapid any shall woman green. Hope they dear who its bred. Smiling nothing affixed he carried it clothes calling he no. Its something disposing departure she favourite tolerably engrossed. Truth short folly court why she their balls. Excellence put unaffected reasonable mrs introduced conviction she. Nay particular delightful but unpleasant for uncommonly who.
20 |
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
332 | # Test results
333 | *.trx
334 |
335 | # Container test results
336 | build/.test-results/
337 |
338 | # Code coverage files
339 | coverage.*
--------------------------------------------------------------------------------
/src/DotNet2019.Host/Diagnostics/HostingDiagnosticHandler.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Hubs;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.AspNetCore.SignalR;
4 | using Microsoft.EntityFrameworkCore.Diagnostics;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 | using Newtonsoft.Json;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Diagnostics;
11 | using System.Text;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace DotNet2019.Host.Diagnostics
16 | {
17 | public class HostingDiagnosticHandler : IHostedService, IObserver>, IObserver, IDisposable
18 | {
19 | private readonly DiagnosticListener _diagnosticListener;
20 | private readonly IServiceScopeFactory _scopeFactory;
21 | private readonly IHubContext _diagnosticsHub;
22 | private IDisposable _subscription;
23 | private List _listenersSubscriptions = new List();
24 | private static Dictionary traces = new Dictionary();
25 | private string requestBegin = "---------- REQUEST {0} BEGIN ----------\n";
26 | private string requestEnd = "---------- REQUEST {0} END ----------\n";
27 |
28 | public HostingDiagnosticHandler(DiagnosticListener diagnosticListener, IServiceScopeFactory scopeFactory, IHubContext diagnosticsHub)
29 | {
30 | _diagnosticListener = diagnosticListener;
31 | _scopeFactory = scopeFactory;
32 | _diagnosticsHub = diagnosticsHub;
33 | }
34 |
35 | public Task StartAsync(CancellationToken cancellationToken)
36 | {
37 | _subscription = DiagnosticListener.AllListeners.Subscribe(this);
38 | return Task.CompletedTask;
39 | }
40 |
41 | public Task StopAsync(CancellationToken cancellationToken)
42 | {
43 | return Task.CompletedTask;
44 | }
45 |
46 | public void OnNext(DiagnosticListener value)
47 | {
48 | _listenersSubscriptions.Add(value.Subscribe(this));
49 | }
50 |
51 | public void OnNext(KeyValuePair @event)
52 | {
53 | switch (@event.Key)
54 | {
55 |
56 | case "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start":
57 | {
58 | var context = GetContext(@event.Value);
59 | var requestId = Guid.NewGuid();
60 | context.Items.Add("RequestId", requestId);
61 |
62 | var builder = new StringBuilder(string.Format(requestBegin, requestId))
63 | .Append($"[HttpStart] Incoming request {context.Request.Path} VERB: {context.Request.Method}\n");
64 | traces[requestId.ToString()] = builder;
65 | }
66 | break;
67 | case "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop":
68 | {
69 | var context = GetContext(@event.Value);
70 | var requestId = context.Items["RequestId"].ToString();
71 |
72 | var trace = (traces[requestId.ToString()]
73 | .Append($"[HttpStop] Request finished with code {context.Response.StatusCode} \n")
74 | .Append(string.Format(requestEnd, requestId))
75 | .ToString());
76 |
77 | Task.Run(async () =>
78 | {
79 | await _diagnosticsHub.Clients.All.SendAsync("SendDiagnotics", trace);
80 | });
81 |
82 | traces.Remove(requestId);
83 | }
84 | break;
85 |
86 | case "Microsoft.AspNetCore.Diagnostics.UnhandledException":
87 | {
88 | var context = GetContext(@event.Value);
89 | var ex = @event.Value as Exception;
90 | var requestId = context.Items["RequestId"].ToString();
91 | traces[requestId].Append($"[Exception] Request {requestId} failed with error {ex.Message}\n");
92 | }
93 | break;
94 | case "Microsoft.AspNetCore.Mvc.AfterActionResult":
95 | {
96 | var actionContext = GetProperty