Creating endpoints with errors

This commit is contained in:
Cristiano
2025-10-17 10:21:13 +02:00
parent 2369cdbc39
commit dde32c7ecd
17 changed files with 277 additions and 13 deletions

View File

@@ -15,14 +15,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.20" />
<PackageReference Include="PasswordGenerator" Version="2.1.0" />
<PackageReference Include="Singulink.Cryptography.PasswordHasher" Version="3.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Endpoints\Comment\" />
<Folder Include="Endpoints\Post\" />
<Folder Include="Endpoints\User\" />
</ItemGroup>
</Project>

View File

@@ -5,5 +5,4 @@ public class CreateUserDto
public string? Username { get; set; }
public string? Email { get; set; }
public string? Password { get; set; }
public string? Salt { get; set; }
}

View File

@@ -0,0 +1,57 @@
using BlogPlatform.DTO.Comment.Request;
using BlogPlatform.DTO.Comment.Response;
using BlogPlatform.Models;
using FastEndpoints;
namespace BlogPlatform.Endpoints.Comment;
public class CreateCommentEndpoint(BlogPlatformDbContext db) : Endpoint<CreateCommentDto,GetCommentDto>
{
public override void Configure()
{
Post("/api/comments");
}
public override async Task HandleAsync(CreateCommentDto req, CancellationToken ct)
{
// Checking if the user and post IDs exist in our database
Models.User? checkingUser = await db.Users.FindAsync(req.UserId);
Models.Post? checkingPost = await db.Posts.FindAsync(req.PostId);
if (checkingUser == null || checkingPost == null)
{
await Send.StringAsync("You must add a comment on a Post that exist and of a User that exists", 400);
return;
}
// User and Post exists adding the comment
Models.Comment newComment = new()
{
Content = req.Content,
PostId = req.PostId,
Post = checkingPost,
UserId = req.UserId,
User = checkingUser,
};
db.Comments.Add(newComment);
await db.SaveChangesAsync(ct);
// Returning the response with DTO
GetCommentDto dto = new()
{
Id = newComment.Id,
Content = newComment.Content,
PostId = newComment.PostId,
PostTitle = checkingPost.Title,
UserId = newComment.UserId,
UserUsername = checkingUser.Username
};
await Send.OkAsync(dto, ct);
}
}

View File

@@ -0,0 +1,48 @@
using BlogPlatform.DTO.Comment.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace BlogPlatform.Endpoints.Comment;
public class GetCommentRequest
{
public int Id { get; set; }
}
public class GetCommentEndpoint(BlogPlatformDbContext db) : Endpoint<GetCommentRequest,GetCommentDto>
{
public override void Configure()
{
Get("/api/comments/{@id}", x => new{x.Id});
}
public override async Task HandleAsync(GetCommentRequest req, CancellationToken ct)
{
Models.Comment? comment = await db.Comments
.Include(comment => comment.User)
.Include(comment => comment.Post)
.FirstOrDefaultAsync(x => x.Id == req.Id);
if (comment == null)
{
await Send.NotFoundAsync(ct);
return;
}
// Returning the response with DTO
GetCommentDto dto = new()
{
Id = comment.Id,
Content = comment.Content,
PostId = comment.PostId,
PostTitle = comment.Post.Title,
UserId = comment.UserId,
UserUsername = comment.User.Username
};
await Send.OkAsync(dto, ct);
}
}

View File

@@ -0,0 +1,52 @@
using BlogPlatform.DTO.Comment.Request;
using BlogPlatform.DTO.Comment.Response;
using BlogPlatform.DTO.Post.Request;
using BlogPlatform.DTO.Post.Response;
using FastEndpoints;
namespace BlogPlatform.Endpoints.Post;
public class CreatePostEndpoint(BlogPlatformDbContext db) : Endpoint<CreatePostDto,GetPostDto>
{
public override void Configure()
{
Post("/api/posts");
}
public override async Task HandleAsync(CreatePostDto req, CancellationToken ct)
{
Models.User checkingUser = await db.Users.FindAsync(req.UserId);
if (checkingUser == null)
{
await Send.StringAsync("You must create a post with an existing user", 400);
return;
}
Models.Post newPost = new()
{
Title = req.Title,
Content = req.Content,
Likes = req.Likes,
UserId = req.UserId,
User = checkingUser
};
db.Posts.Add(newPost);
await db.SaveChangesAsync(ct);
GetPostDto dto = new()
{
Id = newPost.Id,
Title = req.Title,
Content = req.Content,
Likes = req.Likes,
UserId = req.UserId,
UserUsername = checkingUser.Username,
UserEmail = checkingUser.Email,
};
await Send.OkAsync(dto, ct);
}
}

View File

@@ -0,0 +1,44 @@
using BlogPlatform.DTO.Post.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace BlogPlatform.Endpoints.Post;
public class GetPostRequest
{
public int Id { get; set; }
}
public class GetPostEndpoint(BlogPlatformDbContext db) : Endpoint<GetPostRequest,GetPostDto>
{
public override void Configure()
{
Get("/api/posts/{@id}", x => new{x.Id});
}
public override async Task HandleAsync(GetPostRequest req, CancellationToken ct)
{
Models.Post post = await db.Posts
.Include(post => post.User)
.SingleOrDefaultAsync(x => x.Id == req.Id);
if (post == null)
{
await Send.NotFoundAsync(ct);
return;
}
GetPostDto dto = new()
{
Id = post.Id,
Title = req.Title,
Content = req.Content,
Likes = req.Likes,
UserId = req.UserId,
UserUsername = checkingUser.Username,
UserEmail = checkingUser.Email,
};
await Send.OkAsync(dto, ct);
}
}

View File

@@ -0,0 +1,38 @@
using BlogPlatform.DTO.Comment.Request;
using BlogPlatform.DTO.Comment.Response;
using BlogPlatform.DTO.User.Request;
using BlogPlatform.DTO.User.Response;
using BlogPlatform.Models;
using FastEndpoints;
using PasswordGenerator;
namespace BlogPlatform.Endpoints.User;
public class CreateUserEndpoint(BlogPlatformDbContext db) : Endpoint<CreateUserDto,GetUserDto>
{
public override void Configure()
{
Post("/api/users");
AllowAnonymous();
}
public override async Task HandleAsync(CreateUserDto req, CancellationToken ct)
{
string? salt = new Password()
.IncludeLowercase()
.IncludeUppercase()
.IncludeNumeric()
.IncludeSpecial()
.LengthRequired(24)
.Next();
Models.User user = new()
{
Username = req.Username,
Email = req.Email,
Password = Singulink.Cryptography.PasswordHasher(req.Password+ salt, salt),
Salt = salt,
}
}
}

View File

@@ -60,6 +60,10 @@
"target": "Package",
"version": "[8.0.20, )"
},
"PasswordGenerator": {
"target": "Package",
"version": "[2.1.0, )"
},
"Singulink.Cryptography.PasswordHasher": {
"target": "Package",
"version": "[3.0.2, )"

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("BlogPlatform")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3fa5289fb179c6531ef666b66d5fb77eba91f166")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+2369cdbc398561b187eafbf4f208541a0bf6ebd8")]
[assembly: System.Reflection.AssemblyProductAttribute("BlogPlatform")]
[assembly: System.Reflection.AssemblyTitleAttribute("BlogPlatform")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
0f79a8820730bb50db3f741ae20cc71e5fd45452fc8efc9b4577dd707ee51130
399cf2c58589a118a3fb968eb6106a6f5f0387106b36b127abb19a5be52197f6

View File

@@ -982,6 +982,15 @@
"lib/netstandard2.0/Mono.TextTemplating.dll": {}
}
},
"PasswordGenerator/2.1.0": {
"type": "package",
"compile": {
"lib/netstandard2.0/PasswordGenerator.dll": {}
},
"runtime": {
"lib/netstandard2.0/PasswordGenerator.dll": {}
}
},
"Singulink.Cryptography.PasswordHasher/3.0.2": {
"type": "package",
"dependencies": {
@@ -3364,6 +3373,18 @@
"mono.texttemplating.nuspec"
]
},
"PasswordGenerator/2.1.0": {
"sha512": "G70cCeAOYCk/uTuFw5PBGpYY9kmBZyzld7tphachvyU514PowMRDYyKwTefxIHNGWrhXY2d1DV5B1tDEnTtY3A==",
"type": "package",
"path": "passwordgenerator/2.1.0",
"files": [
".nupkg.metadata",
".signature.p7s",
"lib/netstandard2.0/PasswordGenerator.dll",
"passwordgenerator.2.1.0.nupkg.sha512",
"passwordgenerator.nuspec"
]
},
"Singulink.Cryptography.PasswordHasher/3.0.2": {
"sha512": "LJsc3CSD5M4UoIOwf3henpUzjJIv6A6UhEHMKxIqzhqtsOF92eOvEtyc3wyl28sr63xJfb6v7vnM4y69SUYk0g==",
"type": "package",
@@ -4587,6 +4608,7 @@
"Microsoft.EntityFrameworkCore >= 8.0.20",
"Microsoft.EntityFrameworkCore.Design >= 8.0.20",
"Microsoft.EntityFrameworkCore.SqlServer >= 8.0.20",
"PasswordGenerator >= 2.1.0",
"Singulink.Cryptography.PasswordHasher >= 3.0.2",
"Swashbuckle.AspNetCore >= 6.6.2"
]
@@ -4650,6 +4672,10 @@
"target": "Package",
"version": "[8.0.20, )"
},
"PasswordGenerator": {
"target": "Package",
"version": "[2.1.0, )"
},
"Singulink.Cryptography.PasswordHasher": {
"target": "Package",
"version": "[3.0.2, )"

View File

@@ -1,6 +1,6 @@
{
"version": 2,
"dgSpecHash": "J6GHh76bgMQ=",
"dgSpecHash": "O3btQBsvLxE=",
"success": true,
"projectFilePath": "/home/cristiano/Documents/BTS-SIO2/DS-Cristiano/BlogPlatform/BlogPlatform/BlogPlatform.csproj",
"expectedPackageFiles": [
@@ -52,6 +52,7 @@
"/home/cristiano/.nuget/packages/microsoft.sqlserver.server/1.0.0/microsoft.sqlserver.server.1.0.0.nupkg.sha512",
"/home/cristiano/.nuget/packages/microsoft.win32.systemevents/6.0.0/microsoft.win32.systemevents.6.0.0.nupkg.sha512",
"/home/cristiano/.nuget/packages/mono.texttemplating/2.2.1/mono.texttemplating.2.2.1.nupkg.sha512",
"/home/cristiano/.nuget/packages/passwordgenerator/2.1.0/passwordgenerator.2.1.0.nupkg.sha512",
"/home/cristiano/.nuget/packages/singulink.cryptography.passwordhasher/3.0.2/singulink.cryptography.passwordhasher.3.0.2.nupkg.sha512",
"/home/cristiano/.nuget/packages/swashbuckle.aspnetcore/6.6.2/swashbuckle.aspnetcore.6.6.2.nupkg.sha512",
"/home/cristiano/.nuget/packages/swashbuckle.aspnetcore.swagger/6.6.2/swashbuckle.aspnetcore.swagger.6.6.2.nupkg.sha512",

View File

@@ -1 +1 @@
"restore":{"projectUniqueName":"/home/cristiano/Documents/BTS-SIO2/DS-Cristiano/BlogPlatform/BlogPlatform/BlogPlatform.csproj","projectName":"BlogPlatform","projectPath":"/home/cristiano/Documents/BTS-SIO2/DS-Cristiano/BlogPlatform/BlogPlatform/BlogPlatform.csproj","outputPath":"/home/cristiano/Documents/BTS-SIO2/DS-Cristiano/BlogPlatform/BlogPlatform/obj/","projectStyle":"PackageReference","originalTargetFrameworks":["net8.0"],"sources":{"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"FastEndpoints":{"target":"Package","version":"[7.0.1, )"},"Microsoft.AspNetCore.OpenApi":{"target":"Package","version":"[8.0.20, )"},"Microsoft.EntityFrameworkCore":{"target":"Package","version":"[8.0.20, )"},"Microsoft.EntityFrameworkCore.Design":{"include":"Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive","suppressParent":"All","target":"Package","version":"[8.0.20, )"},"Microsoft.EntityFrameworkCore.SqlServer":{"target":"Package","version":"[8.0.20, )"},"Singulink.Cryptography.PasswordHasher":{"target":"Package","version":"[3.0.2, )"},"Swashbuckle.AspNetCore":{"target":"Package","version":"[6.6.2, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.AspNetCore.App":{"privateAssets":"none"},"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"/usr/lib/dotnet/sdk/8.0.121/PortableRuntimeIdentifierGraph.json"}}
"restore":{"projectUniqueName":"/home/cristiano/Documents/BTS-SIO2/DS-Cristiano/BlogPlatform/BlogPlatform/BlogPlatform.csproj","projectName":"BlogPlatform","projectPath":"/home/cristiano/Documents/BTS-SIO2/DS-Cristiano/BlogPlatform/BlogPlatform/BlogPlatform.csproj","outputPath":"/home/cristiano/Documents/BTS-SIO2/DS-Cristiano/BlogPlatform/BlogPlatform/obj/","projectStyle":"PackageReference","originalTargetFrameworks":["net8.0"],"sources":{"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"FastEndpoints":{"target":"Package","version":"[7.0.1, )"},"Microsoft.AspNetCore.OpenApi":{"target":"Package","version":"[8.0.20, )"},"Microsoft.EntityFrameworkCore":{"target":"Package","version":"[8.0.20, )"},"Microsoft.EntityFrameworkCore.Design":{"include":"Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive","suppressParent":"All","target":"Package","version":"[8.0.20, )"},"Microsoft.EntityFrameworkCore.SqlServer":{"target":"Package","version":"[8.0.20, )"},"PasswordGenerator":{"target":"Package","version":"[2.1.0, )"},"Singulink.Cryptography.PasswordHasher":{"target":"Package","version":"[3.0.2, )"},"Swashbuckle.AspNetCore":{"target":"Package","version":"[6.6.2, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.AspNetCore.App":{"privateAssets":"none"},"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"/usr/lib/dotnet/sdk/8.0.121/PortableRuntimeIdentifierGraph.json"}}

View File

@@ -1 +1 @@
17606848202915370
17606871115387461

View File

@@ -1 +1 @@
17606848204795382
17606871117347472