Skip to content

Commit 3ddb2dd

Browse files
author
Sergey Tregub
committed
Migrate project to .Net Core 3.1
1 parent 432f053 commit 3ddb2dd

4 files changed

Lines changed: 140 additions & 126 deletions

File tree

ProjectTemplates/ReferenceProject/Middleware/ExceptionMiddleware.cs

Lines changed: 87 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,104 +6,105 @@
66
using Newtonsoft.Json;
77
using Microsoft.AspNetCore.Hosting;
88
using ReferenceProject.Exceptions;
9+
using Microsoft.Extensions.Hosting;
910

1011
namespace ReferenceProject.Middleware
1112
{
12-
/// <summary>
13-
/// Middleware to handle exceptions.
14-
/// It separates exceptions based on their type and returns different status codes and answers based on it, instead of 500 Internal Server Error code in all cases.
15-
/// In addition, it writes them in the log.
16-
/// </summary>
17-
/// <remarks>
18-
/// There is another way to do this - an exception filter.
19-
/// However, a middleware is a preferred way to achieve this according to the official documentation.
20-
/// To learn more see https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.1#exception-filters
21-
///
22-
/// See also: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#unhandled-exceptions-handling
23-
/// </remarks>
24-
public class ExceptionMiddleware
25-
{
26-
RequestDelegate Next { get; }
27-
ILogger Logger { get; }
28-
IHostingEnvironment Environment { get; }
13+
/// <summary>
14+
/// Middleware to handle exceptions.
15+
/// It separates exceptions based on their type and returns different status codes and answers based on it, instead of 500 Internal Server Error code in all cases.
16+
/// In addition, it writes them in the log.
17+
/// </summary>
18+
/// <remarks>
19+
/// There is another way to do this - an exception filter.
20+
/// However, a middleware is a preferred way to achieve this according to the official documentation.
21+
/// To learn more see https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.1#exception-filters
22+
///
23+
/// See also: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#unhandled-exceptions-handling
24+
/// </remarks>
25+
public class ExceptionMiddleware
26+
{
27+
RequestDelegate Next { get; }
28+
ILogger Logger { get; }
29+
IWebHostEnvironment Environment { get; }
2930

30-
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger, IHostingEnvironment environment)
31-
{
32-
Environment = environment ?? throw new ArgumentNullException(nameof(environment));
33-
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
34-
Next = next ?? throw new ArgumentNullException(nameof(next));
35-
}
31+
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger, IWebHostEnvironment environment)
32+
{
33+
Environment = environment ?? throw new ArgumentNullException(nameof(environment));
34+
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
35+
Next = next ?? throw new ArgumentNullException(nameof(next));
36+
}
3637

37-
public async Task InvokeAsync(HttpContext context)
38-
{
39-
var body = context.Response.Body;
40-
try
41-
{
42-
await Next(context);
43-
}
44-
catch (Exception ex)
45-
{
46-
// If context.Response.HasStarted == true, then we can't write to the response stream anymore. So we have to restore the body.
47-
// If we don't do that we get an exception.
48-
context.Response.Body = body;
49-
await HandleExceptionAsync(context, ex);
50-
}
51-
}
38+
public async Task InvokeAsync(HttpContext context)
39+
{
40+
var body = context.Response.Body;
41+
try
42+
{
43+
await Next(context);
44+
}
45+
catch (Exception ex)
46+
{
47+
// If context.Response.HasStarted == true, then we can't write to the response stream anymore. So we have to restore the body.
48+
// If we don't do that we get an exception.
49+
context.Response.Body = body;
50+
await HandleExceptionAsync(context, ex);
51+
}
52+
}
5253

53-
async Task HandleExceptionAsync(HttpContext context, Exception ex)
54-
{
55-
int statusCode = 500;
54+
async Task HandleExceptionAsync(HttpContext context, Exception ex)
55+
{
56+
int statusCode = 500;
5657

57-
context.Response.ContentType = "application/json";
58-
context.Response.StatusCode = statusCode;
58+
context.Response.ContentType = "application/json";
59+
context.Response.StatusCode = statusCode;
5960

60-
// We can decide what the status code should return
61-
if (ex is KeyNotFoundException)
62-
{
63-
context.Response.StatusCode = StatusCodes.Status404NotFound;
64-
}
65-
else if (ex is DuplicateKeyException)
66-
{
67-
context.Response.StatusCode = StatusCodes.Status400BadRequest;
68-
}
61+
// We can decide what the status code should return
62+
if (ex is KeyNotFoundException)
63+
{
64+
context.Response.StatusCode = StatusCodes.Status404NotFound;
65+
}
66+
else if (ex is DuplicateKeyException)
67+
{
68+
context.Response.StatusCode = StatusCodes.Status400BadRequest;
69+
}
6970

70-
await context.Response.WriteAsync(
71-
JsonConvert.SerializeObject(
72-
new ErrorResponse(ex, Environment.IsDevelopment())));
71+
await context.Response.WriteAsync(
72+
JsonConvert.SerializeObject(
73+
new ErrorResponse(ex, Environment.IsDevelopment())));
7374

74-
if (context.Response.StatusCode == StatusCodes.Status500InternalServerError)
75-
{
76-
Logger.LogError(ex, "Unhandled exception occurred");
77-
}
78-
else
79-
{
80-
Logger.LogDebug(ex, "Unhandled exception occurred");
81-
}
82-
}
75+
if (context.Response.StatusCode == StatusCodes.Status500InternalServerError)
76+
{
77+
Logger.LogError(ex, "Unhandled exception occurred");
78+
}
79+
else
80+
{
81+
Logger.LogDebug(ex, "Unhandled exception occurred");
82+
}
83+
}
8384

84-
class ErrorResponse
85-
{
86-
public ErrorResponse(Exception ex, bool includeFullExceptionInfo)
87-
{
88-
Error = new ExceptionDescription(ex);
89-
if(includeFullExceptionInfo)
90-
{
91-
Error.Exception = ex;
92-
}
93-
}
85+
class ErrorResponse
86+
{
87+
public ErrorResponse(Exception ex, bool includeFullExceptionInfo)
88+
{
89+
Error = new ExceptionDescription(ex);
90+
if (includeFullExceptionInfo)
91+
{
92+
Error.Exception = ex;
93+
}
94+
}
9495

95-
public ExceptionDescription Error { get; set; }
96-
}
96+
public ExceptionDescription Error { get; set; }
97+
}
9798

98-
class ExceptionDescription
99-
{
100-
public ExceptionDescription(Exception ex)
101-
{
102-
Message = ex.Message;
103-
}
99+
class ExceptionDescription
100+
{
101+
public ExceptionDescription(Exception ex)
102+
{
103+
Message = ex.Message;
104+
}
104105

105-
public string Message { get; set; }
106-
public Exception Exception { get; set; }
107-
}
108-
}
106+
public string Message { get; set; }
107+
public Exception Exception { get; set; }
108+
}
109+
}
109110
}
Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
1-
using Microsoft.AspNetCore;
1+
using Autofac.Extensions.DependencyInjection;
2+
using Microsoft.AspNetCore;
23
using Microsoft.AspNetCore.Hosting;
34
using Microsoft.Extensions.Logging;
45
using Microsoft.Extensions.Configuration;
56
using Serilog;
67

7-
using Autofac.Extensions.DependencyInjection;
8-
98
namespace ReferenceProject
109
{
11-
public class Program
12-
{
13-
public static void Main(string[] args)
14-
{
15-
CreateWebHostBuilder(args).Build().Run();
16-
}
10+
public class Program
11+
{
12+
public static void Main(string[] args)
13+
{
14+
CreateHostBuilder(args).Build().Run();
15+
}
1716

18-
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
19-
WebHost.CreateDefaultBuilder(args)
20-
.ConfigureServices(services => services.AddAutofac())
21-
.ConfigureLogging((context, logging) =>
22-
{
23-
logging.ClearProviders();
17+
// TODO: WebHost -> Host: https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#hostbuilder-replaces-webhostbuilder
18+
public static IWebHostBuilder CreateHostBuilder(string[] args) =>
19+
WebHost.CreateDefaultBuilder(args)
20+
.ConfigureServices(services => services.AddAutofac())
21+
.ConfigureLogging((context, logging) =>
22+
{
23+
logging.ClearProviders();
2424

25-
/*
25+
/*
2626
* You can use a global logger as this, but I don't recommend this way
2727
* More information: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#logging
2828
Log.Logger = new LoggerConfiguration()
2929
.ReadFrom.Configuration(context.Configuration)
3030
.CreateLogger();
3131
*/
3232

33-
logging.AddSerilog(new LoggerConfiguration()
34-
.ReadFrom.Configuration(context.Configuration)
35-
.CreateLogger());
36-
})
37-
.ConfigureAppConfiguration(x =>
38-
{
39-
x.AddEnvironmentVariables();
40-
})
41-
.UseStartup<Startup>();
42-
}
33+
logging.AddSerilog(new LoggerConfiguration()
34+
.ReadFrom.Configuration(context.Configuration)
35+
.CreateLogger());
36+
})
37+
.ConfigureAppConfiguration(x =>
38+
{
39+
x.AddEnvironmentVariables();
40+
})
41+
.UseStartup<Startup>();
42+
}
4343
}

ProjectTemplates/ReferenceProject/ReferenceProject.csproj

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp2.2</TargetFramework>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
55
<LangVersion>latest</LangVersion>
66
</PropertyGroup>
77

@@ -33,8 +33,7 @@
3333
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="5.0.1" />
3434
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
3535
<PackageReference Include="DotNetEnv" Version="1.4.0" />
36-
<PackageReference Include="Microsoft.AspNetCore.App" />
37-
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
36+
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
3837
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
3938
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
4039
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />

ProjectTemplates/ReferenceProject/Startup.cs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
using Autofac.Configuration;
33
using AutoMapper;
44
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
56
using Microsoft.AspNetCore.Hosting;
67
using Microsoft.AspNetCore.Http;
78
using Microsoft.AspNetCore.Mvc;
89
using Microsoft.AspNetCore.Mvc.Infrastructure;
910
using Microsoft.AspNetCore.Mvc.Routing;
1011
using Microsoft.Extensions.Configuration;
1112
using Microsoft.Extensions.DependencyInjection;
13+
using Microsoft.Extensions.Hosting;
1214
using Microsoft.Extensions.Logging;
1315
using Newtonsoft.Json;
1416
using Newtonsoft.Json.Serialization;
@@ -28,7 +30,7 @@ public class Startup
2830
{
2931
ILogger<Startup> Logger { get; }
3032

31-
public Startup(IConfiguration configuration, IHostingEnvironment env, ILogger<Startup> logger)
33+
public Startup(IConfiguration configuration, IWebHostEnvironment env, ILogger<Startup> logger)
3234
{
3335
Logger = logger;
3436
Startup.Configuration = configuration;
@@ -76,6 +78,9 @@ public void ConfigureServices(IServiceCollection services)
7678
options.Filters.Add(new CacheControlFilter()); // Add "Cache-Control" header. See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#cache-control
7779
})
7880
.AddApiExplorer()
81+
/*
82+
* TODO: Must be removed
83+
* This code is obsolete now: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#differences-in-default-jsonserializer-behavior-compared-to-newtonsoftjson
7984
.AddJsonFormatters() // See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#content-formatting
8085
.AddJsonOptions(options =>
8186
{
@@ -88,13 +93,17 @@ public void ConfigureServices(IServiceCollection services)
8893
#else
8994
options.SerializerSettings.Formatting = Formatting.None;
9095
#endif
91-
})
92-
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
96+
})*/
97+
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
9398

9499
services
95100
.AddAutoMapper(typeof(Startup)) // Check out Configuration/AutoMapperProfiles/DefaultProfile to do actual configuration. See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#automapper
96101
.AddSwagger(); // Check out Configuration/DependenciesConfig.cs/AddSwagger to do actual configuration. See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#documenting-api
97-
}
102+
103+
services.AddRouting();
104+
services.AddControllers();
105+
services.AddHealthChecks();
106+
}
98107

99108
/// <summary>
100109
/// Configure Autofac DI-container
@@ -135,26 +144,31 @@ public void ConfigureProductionContainer(ContainerBuilder builder)
135144
}
136145

137146
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
138-
public void Configure(IApplicationBuilder app/*, IHostingEnvironment env*/)
147+
public void Configure(IApplicationBuilder app)
139148
{
140149
// Use an exception handler middleware before any other handlers
141150
// See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#unhandled-exceptions-handling
142151
app.UseExceptionHandler();
143152

144-
// See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#cross-origin-resource-sharing-cors-and-preflight-requests
145-
app.UseCors(builder => builder
153+
app.UseRouting();
154+
155+
// See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#cross-origin-resource-sharing-cors-and-preflight-requests
156+
app.UseCors(builder => builder
146157
.AllowAnyOrigin()
147158
.AllowAnyMethod()
148-
.AllowAnyHeader()
149-
.AllowCredentials());
159+
.AllowAnyHeader());
150160

151161
app
152162
.UseOptionsVerbHandler() // Options verb handler must be added after CORS. See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#cross-origin-resource-sharing-cors-and-preflight-requests
153163
.UseSwaggerWithOptions(); // Check out Configuration/MiddlewareConfig.cs/UseSwaggerWithOptions to do actual configuration. See: https://github.com/drwatson1/AspNet-Core-REST-Service/wiki#documenting-api
154164

155-
app.UseMvcWithDefaultRoute();
165+
app.UseEndpoints(endpoints =>
166+
{
167+
endpoints.MapDefaultControllerRoute();
168+
endpoints.MapHealthChecks("/health"); // TODO: Must be documented: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-3.1
169+
});
156170

157-
Logger.LogInformation("Server started");
171+
Logger.LogInformation("Server started");
158172
}
159173
}
160174
}

0 commit comments

Comments
 (0)