Skip to content

Commit 77754d8

Browse files
.net 7 updates minimal api samples
1 parent d4e4609 commit 77754d8

32 files changed

+85
-50
lines changed

5_More/Services/MinimalAPIWithGroupsAndTypedResults/Endpoints/GameEndpoints.cs 5_More/Services/GamesApiDotnet7/Endpoints/GameEndpoints.cs

+12-29
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
using Codebreaker.Utilities;
22

33
using Microsoft.AspNetCore.Http.HttpResults;
4+
using Microsoft.AspNetCore.Mvc;
45

56
namespace Codebreaker.Endpoints;
67

7-
// polymorphic hierarchy https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-7-0
8-
98
public static class GameEndpoints
109
{
1110
public static IEndpointRouteBuilder MapGameEndpoints(this IEndpointRouteBuilder routes, ILogger logger)
1211
{
1312
var group = routes.MapGroup("/games")
14-
.WithTags(nameof(Game));
13+
.WithTags("Gameplay")
14+
.AddEndpointFilter<LoggingFilter>();
1515

1616
group.MapGet("/", async (IGamesService gamesService) =>
1717
{
@@ -35,7 +35,7 @@ public static IEndpointRouteBuilder MapGameEndpoints(this IEndpointRouteBuilder
3535
.WithSummary("Gets a game by the given id")
3636
.WithOpenApi(op =>
3737
{
38-
op.Parameters[0].Description = "The id of the game to get";
38+
op.Parameters[0].Description = "The game-id to request a game run";
3939
return op;
4040
});
4141

@@ -46,18 +46,15 @@ public static IEndpointRouteBuilder MapGameEndpoints(this IEndpointRouteBuilder
4646
try
4747
{
4848
game = await gamesService.CreateGameAsync(request.GameType, request.PlayerName);
49+
50+
CreateGameResponse createGameResponse = new(game.GameId, game.GameType, game.PlayerName);
51+
return TypedResults.Created($"/{game.GameId}", createGameResponse);
4952
}
5053
catch (InvalidGameException ex) when (ex.HResult == 4000)
5154
{
5255
logger.LogError("Game Type not found {gametype}", request.GameType);
53-
54-
// InvalidGameRequest invalidRequest = new("Gametype does not exist, valid types:", new[] { GameTypes.Game6x4, GameTypes.Game8x5, GameTypes.Game5x5x4, GameTypes.Game6x4Simple });
55-
// return TypedResults.BadRequest(invalidRequest);
5656
return TypedResults.BadRequest();
5757
}
58-
59-
CreateGameResponse createGameResponse = new(game.GameId, game.GameType, game.PlayerName);
60-
return TypedResults.Created($"/{game.GameId}", createGameResponse);
6158
})
6259
.WithName("CreateGame")
6360
.WithSummary("Creates and starts a game")
@@ -70,30 +67,16 @@ public static IEndpointRouteBuilder MapGameEndpoints(this IEndpointRouteBuilder
7067
// Create a move for a game
7168
group.MapPost("/{gameId:guid}/moves", async Task<Results<Ok<SetMoveResponse>, BadRequest<string>, NotFound>> (Guid gameId, SetMoveRequest request, IGamesService gamesService) =>
7269
{
73-
if (gameId != request.GameId)
74-
{
75-
return TypedResults.BadRequest("id does not match");
76-
}
77-
if (request.ColorFields == default && request.ShapeAndColorFields == default)
78-
{
79-
return TypedResults.BadRequest("Either fill ColorFields or ShapeAndColorFields");
80-
}
81-
try
82-
{
83-
SetMoveResponse response = await gamesService.SetMoveAsync(request);
84-
return TypedResults.Ok(response);
85-
}
86-
catch (GameNotFoundException)
87-
{
88-
logger.LogError("Game {gameid} not found", request.GameId);
89-
return TypedResults.NotFound();
90-
}
70+
SetMoveResponse response = await gamesService.SetMoveAsync(request);
71+
return TypedResults.Ok(response);
9172
})
73+
.AddEndpointFilter<GameMoveValidationFilter>()
74+
.AddEndpointFilter<GameMoveExceptionFilter>()
9275
.WithName("SetMove")
9376
.WithSummary("Sets a move with a game")
9477
.WithOpenApi(op =>
9578
{
96-
op.RequestBody.Description = "The move to set";
79+
op.RequestBody.Description = "The game move consisting of values for every available hole";
9780
return op;
9881
});
9982

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Codebreaker.Utilities;
2+
3+
namespace Codebreaker.Endpoints;
4+
5+
public class GameMoveExceptionFilter : IEndpointFilter
6+
{
7+
private readonly ILogger _logger;
8+
public GameMoveExceptionFilter(ILogger<GameMoveExceptionFilter> logger)
9+
{
10+
_logger = logger;
11+
}
12+
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
13+
{
14+
SetMoveRequest request = context.GetArgument<SetMoveRequest>(1);
15+
try
16+
{
17+
return await next(context);
18+
}
19+
catch (GameNotFoundException ex)
20+
{
21+
_logger.LogError(ex, "Game {gameid} not found", request.GameId);
22+
return TypedResults.NotFound();
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Codebreaker.Endpoints;
2+
3+
public class GameMoveValidationFilter : IEndpointFilter
4+
{
5+
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
6+
{
7+
Guid id = context.GetArgument<Guid>(0);
8+
SetMoveRequest request = context.GetArgument<SetMoveRequest>(1);
9+
if (id != request.GameId)
10+
{
11+
return TypedResults.BadRequest("id does not match");
12+
}
13+
14+
if (request.ColorFields == default && request.ShapeAndColorFields == default)
15+
{
16+
return TypedResults.BadRequest("Either fill ColorFields or ShapeAndColorFields");
17+
}
18+
return await next(context);
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.AspNetCore.Http.Extensions;
2+
3+
namespace Codebreaker.Endpoints;
4+
5+
public class LoggingFilter : IEndpointFilter
6+
{
7+
private readonly ILogger _logger;
8+
public LoggingFilter(ILoggerFactory loggerFactory)
9+
{
10+
_logger = loggerFactory.CreateLogger<LoggingFilter>();
11+
}
12+
13+
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
14+
{
15+
_logger.LogTrace("{uri} called using {method}", context.HttpContext.Request.GetDisplayUrl(), context.HttpContext.Request.Method);
16+
return await next(context);
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
4-
VisualStudioVersion = 17.6.33513.286
4+
VisualStudioVersion = 17.6.33620.401
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalAPIWithGroupingAndTypedResults", "MinimalAPIWithGroupingAndTypedResults.csproj", "{0DDD38E3-1DB6-4F81-BDA6-218422B9542B}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GamesApiDotnet7", "GamesApiDotnet7.csproj", "{A412B5A8-C523-41A6-87AE-9D3286001C99}"
77
EndProject
88
Global
99
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1010
Debug|Any CPU = Debug|Any CPU
1111
Release|Any CPU = Release|Any CPU
1212
EndGlobalSection
1313
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14-
{0DDD38E3-1DB6-4F81-BDA6-218422B9542B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15-
{0DDD38E3-1DB6-4F81-BDA6-218422B9542B}.Debug|Any CPU.Build.0 = Debug|Any CPU
16-
{0DDD38E3-1DB6-4F81-BDA6-218422B9542B}.Release|Any CPU.ActiveCfg = Release|Any CPU
17-
{0DDD38E3-1DB6-4F81-BDA6-218422B9542B}.Release|Any CPU.Build.0 = Release|Any CPU
14+
{A412B5A8-C523-41A6-87AE-9D3286001C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{A412B5A8-C523-41A6-87AE-9D3286001C99}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{A412B5A8-C523-41A6-87AE-9D3286001C99}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{A412B5A8-C523-41A6-87AE-9D3286001C99}.Release|Any CPU.Build.0 = Release|Any CPU
1818
EndGlobalSection
1919
GlobalSection(SolutionProperties) = preSolution
2020
HideSolutionNode = FALSE
2121
EndGlobalSection
2222
GlobalSection(ExtensibilityGlobals) = postSolution
23-
SolutionGuid = {489BE7D4-744F-4FBE-B7B9-43C80F2EF9CA}
23+
SolutionGuid = {6CA85128-AF22-4304-A42B-3F961DF88FA2}
2424
EndGlobalSection
2525
EndGlobal

5_More/Services/MinimalAPIWithGroupsAndTypedResults/Program.cs 5_More/Services/GamesApiDotnet7/Program.cs

+3-14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Microsoft.AspNetCore.Http.HttpResults;
12
using Microsoft.AspNetCore.Http.Json;
23

34
using System.Text.Json;
@@ -14,6 +15,7 @@
1415
{
1516
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
1617
});
18+
1719
builder.Services.Configure<JsonOptions>(options =>
1820
{
1921
options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault;
@@ -23,7 +25,7 @@
2325
builder.Services.AddSingleton<IGamesRepository, InMemoryGamesRepository>();
2426
builder.Services.AddSingleton<GamesFactory>();
2527
builder.Services.AddTransient<IGamesService, GamesService>();
26-
28+
Results<Ok, Created, Ok>
2729
var app = builder.Build();
2830

2931
// Configure the HTTP request pipeline.
@@ -37,17 +39,4 @@
3739

3840
app.MapGameEndpoints(app.Logger);
3941

40-
SetMoveRequest req = new(Guid.NewGuid(), GameType.Game6x4, 1)
41-
{
42-
ColorFields = new List<ColorField>
43-
{
44-
new("red"),
45-
new("green"),
46-
new("blue"),
47-
new("blue")
48-
}
49-
};
50-
string json = JsonSerializer.Serialize(req);
51-
52-
5342
app.Run();

0 commit comments

Comments
 (0)