-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
478 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net9.0</TargetFramework> | ||
<Nullable>enable</Nullable> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.*" /> | ||
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="9.0.*"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.*" /> | ||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.*" /> | ||
<PackageReference Include="Microsoft.OpenApi" Version="1.6.*" /> | ||
<PackageReference Include="Scalar.AspNetCore" Version="2.0.9" /> | ||
<PackageReference Include="Serilog" Version="4.2.0" /> | ||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" /> | ||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.*" /> | ||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.*" /> | ||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.*" /> | ||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" /> | ||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="Dtos\" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
@ControllerWebAPI_HostAddress = http://localhost:5170 | ||
|
||
GET {{ControllerWebAPI_HostAddress}}/weatherforecast/ | ||
Accept: application/json | ||
|
||
### |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
namespace ControllerWebAPI.Models; | ||
|
||
public class Game | ||
{ | ||
public required string Id { get; set; } | ||
|
||
public required string Name { get; set; } | ||
|
||
public DateOnly? ReleaseDate { get; set; } | ||
|
||
public IEnumerable<string>? Genres { get; set; } | ||
|
||
public string? Publisher { get; set; } | ||
|
||
public string? Developer { get; set; } | ||
|
||
public string? Description { get; set; } | ||
|
||
public Game(string Id, string Name) | ||
{ | ||
this.Id = Id; | ||
this.Name = Name; | ||
} | ||
|
||
public Game() | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
using ControllerWebAPI.Models; | ||
using ControllerWebAPI.Services; | ||
using Microsoft.AspNetCore.Mvc.Infrastructure; | ||
|
||
namespace ControllerWebAPI.Controllers; | ||
|
||
[ApiController] | ||
// Controller is a class that handles HTTP requests, and it is a collection of actions. | ||
// ControllerBase is a base class for an MVC controller without view support. So, it is used to create Web APIs. | ||
public class GameController : ControllerBase | ||
{ | ||
private readonly GameService _gameService; | ||
|
||
public GameController(GameService gameService) | ||
{ | ||
_gameService = gameService; | ||
} | ||
|
||
[HttpGet("/games")] | ||
public async Task<IEnumerable<Game>> Index() | ||
{ | ||
return await Task.FromResult(_gameService.Games); | ||
} | ||
|
||
|
||
// These are all actions | ||
[HttpGet("game/{id}")] | ||
public async Task<ActionResult<Game>> Show(string id) | ||
{ | ||
var game = _gameService.GetGameById(id); | ||
// If found | ||
return game != null ? await Task.FromResult(game) : NotFound(); | ||
} | ||
|
||
[HttpPost("/game")] | ||
public async Task<ActionResult<Game>> Add([FromBody] Game newGame) | ||
{ | ||
if (!ModelState.IsValid) return BadRequest(new ValidationProblemDetails(ModelState)); | ||
|
||
if (_gameService.Games.Any(_games => _games.Id == newGame.Id)) | ||
{ | ||
// Return bad request by Problem method with ProblemDetails type | ||
// If Problem method is invoked with ProblemDetails related parameters, | ||
// it will return a decent ProblemDetails object with supplied parameters and default values for other properties. | ||
return Problem(detail: $"Game with id {newGame.Id} already exists.", statusCode: StatusCodes.Status400BadRequest); | ||
} | ||
|
||
_gameService.AddGame(newGame); | ||
return await Task.FromResult(CreatedAtAction(nameof(Show), new { id = newGame.Id }, newGame)); | ||
} | ||
|
||
[HttpDelete("/game/{id}")] | ||
public async Task<ActionResult> Delete([FromRoute] string id) | ||
{ | ||
var result = _gameService.GetGameById(id); | ||
if (result == null) | ||
return NotFound(); | ||
_gameService.DeleteGame(id); | ||
|
||
return await Task.FromResult(NoContent()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
namespace ControllerWebAPI.Services; | ||
|
||
using ControllerWebAPI; | ||
using ControllerWebAPI.Models; | ||
|
||
public class GameService | ||
{ | ||
private readonly List<Game> _games = | ||
[ | ||
new Game { Id = "aoe2", Name = "Age of Empires 2: Definitive Edition" }, | ||
new Game { Id = "aoe3", Name = "Age of Empires 3: Definitive Edition" }, | ||
new Game { Id = "aom", Name = "Age of Mythology: Retold" }, | ||
new Game { Id = "sdv", Name = "Stardew Valley" }, | ||
new Game { Id = "ts3", Name = "The Sims 3" }, | ||
new Game { Id = "ts2", Name = "The Sims 2" }, | ||
new Game { Id = "ets", Name = "Euro Truck Simulator 2" }, | ||
new Game { Id = "f4", Name = "Fallout 4" }, | ||
new Game { Id = "f3", Name = "Fallout 3" }, | ||
new Game { Id = "fnv", Name = "Fallout: New Vegas" }, | ||
new Game { Id = "tropico4", Name = "Tropico 4" }, | ||
new Game { Id = "halo", Name = "Halo: Master Chief Collection" }, | ||
]; | ||
|
||
public IEnumerable<Game> Games => _games; | ||
|
||
public IEnumerable<Game> GetAllGames() => _games; | ||
|
||
public Game? GetGameById(string id) => _games.FirstOrDefault(g => g.Id == id); | ||
|
||
public void AddGame(Game newGame) => _games.Add(newGame); | ||
|
||
public void DeleteGame(string id) => _games.Remove(_games.First(g => g.Id == id)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
using Microsoft.AspNetCore.Builder; | ||
using ControllerWebAPI; | ||
using System.Text.Json; | ||
using Serilog; | ||
using Serilog.Events; | ||
using ControllerWebAPI.Controllers; | ||
using ControllerWebAPI.Services; | ||
using System.Diagnostics; | ||
|
||
var configuration = new ConfigurationBuilder() | ||
.SetBasePath(Directory.GetCurrentDirectory()) | ||
.AddEnvironmentVariables() | ||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) | ||
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development"}.json", | ||
optional: true, reloadOnChange: true) | ||
.Build(); | ||
|
||
Log.Logger = new LoggerConfiguration() | ||
.ReadFrom.Configuration(configuration) | ||
.CreateLogger(); | ||
|
||
try | ||
{ | ||
Log.Information("Starting the host..."); | ||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
builder.Configuration.AddConfiguration(configuration); | ||
builder.Services.AddSerilog(); | ||
|
||
// Routing configuration | ||
builder.Services.Configure<RouteOptions>(o => | ||
{ | ||
o.LowercaseUrls = true; | ||
o.AppendTrailingSlash = true; | ||
o.LowercaseQueryStrings = true; | ||
}); | ||
|
||
// JSON configuration | ||
builder.Services.ConfigureHttpJsonOptions(o => | ||
{ | ||
o.SerializerOptions.AllowTrailingCommas = false; | ||
o.SerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; | ||
o.SerializerOptions.PropertyNameCaseInsensitive = true; | ||
}); | ||
|
||
// Enable Scope Validation always (By default, it is only enabled in development) | ||
builder.Host.UseDefaultServiceProvider(o => | ||
{ | ||
o.ValidateScopes = true; | ||
o.ValidateOnBuild = true; | ||
}); | ||
|
||
builder.Services.AddProblemDetails(config => | ||
{ | ||
config.CustomizeProblemDetails = (ctx) => | ||
{ | ||
ctx.ProblemDetails.Type ??= "https://tools.ietf.org/html/rfc7231#section-6.5.4"; | ||
if (!ctx.ProblemDetails.Extensions.ContainsKey("traceId")) | ||
{ | ||
var tId = Activity.Current?.Id ?? ctx.HttpContext.TraceIdentifier; | ||
ctx.ProblemDetails.Extensions.Add(new KeyValuePair<string, object?>("traceId", tId)); | ||
} | ||
}; | ||
}); | ||
builder.Services.AddAntiforgery(); | ||
if (builder.Environment.IsDevelopment()) | ||
{ | ||
builder.Services.AddHealthChecks(); | ||
} | ||
|
||
|
||
// Add services to the container. | ||
builder.Services.AddSingleton<GameService, GameService>(); | ||
|
||
// AddControllers and MapControllers for MVC Web API. | ||
builder.Services.AddControllers(); | ||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi | ||
builder.Services.AddEndpointsApiExplorer(); | ||
builder.Services.AddOpenApi(); | ||
|
||
var app = builder.Build(); | ||
app.UseRouting(); | ||
app.UseSerilogRequestLogging((opts) => | ||
{ | ||
opts.MessageTemplate = "{Protocol} {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms"; | ||
opts.GetMessageTemplateProperties = (HttpContext httpContext, string requestPath, double elapsedMs, int statusCode) => | ||
[ | ||
new LogEventProperty("Protocol", new ScalarValue(httpContext.Request.Protocol)), | ||
new LogEventProperty("RequestMethod", new ScalarValue(httpContext.Request.Method)), | ||
new LogEventProperty("RequestPath", new ScalarValue(requestPath)), | ||
new LogEventProperty("StatusCode", new ScalarValue(statusCode)), | ||
new LogEventProperty("Elapsed", new ScalarValue(elapsedMs)) | ||
]; | ||
}); | ||
|
||
// Configure the HTTP request pipeline. | ||
if (app.Environment.IsDevelopment()) | ||
{ | ||
app.MapOpenApi(); | ||
app.UseDeveloperExceptionPage(); | ||
} | ||
else | ||
{ | ||
app.UseExceptionHandler("/Error"); | ||
app.UseHsts(); | ||
} | ||
|
||
app.UseHttpsRedirection(); | ||
app.UseCors(); | ||
app.UseAntiforgery(); | ||
app.UseStatusCodePages(); | ||
app.UseAuthorization(); | ||
app.MapControllers(); | ||
|
||
app.MapGet("/", () => "Hello World!"); | ||
|
||
app.Run(); | ||
} | ||
catch (Exception ex) when (ex is not HostAbortedException && ex.Source != "Microsoft.EntityFrameworkCore.Design") | ||
{ | ||
Log.Fatal(ex, "The WebApplication host terminated unexpectedly..."); | ||
} | ||
catch (HostAbortedException ex) when (ex.Source != "Microsoft.EntityFrameworkCore.Design") | ||
{ | ||
Log.Fatal(ex, "The WebApplication host is aborted..."); | ||
} | ||
finally | ||
{ | ||
Log.Information("Closing the logger..."); | ||
Log.CloseAndFlush(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"profiles": { | ||
"ControllerWebAPI-Dev": { | ||
"commandName": "Project", | ||
"launchBrowser": true, | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
}, | ||
"applicationUrl": "https://localhost:20621;http://localhost:10327", | ||
"dotnetRunMessages": true | ||
}, | ||
"ControllerWebAPI-Production": { | ||
"commandName": "Project", | ||
"launchBrowser": true, | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Production" | ||
}, | ||
"dotnetRunMessages": true, | ||
"applicationUrl": "https://localhost:5929;http://localhost:5950" | ||
} | ||
}, | ||
"$schema": "http://json.schemastore.org/launchsettings.json" | ||
} |
Oops, something went wrong.