Init
This commit is contained in:
59
Prefab/Endpoints/ApiProblemDetails.cs
Normal file
59
Prefab/Endpoints/ApiProblemDetails.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Prefab.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// Shared helpers for building consistent HTTP problem responses across modules.
|
||||
/// </summary>
|
||||
public static class ApiProblemDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a 404 <see cref="ProblemDetails"/> payload with standard metadata and resource identifiers.
|
||||
/// </summary>
|
||||
/// <param name="resource">Logical name of the missing resource (for example, "Product").</param>
|
||||
/// <param name="identifier">Identifier value supplied by the caller.</param>
|
||||
/// <param name="detail">Human-readable explanation for logging or the client.</param>
|
||||
public static ProblemDetails NotFound(string resource, string identifier, string detail) =>
|
||||
new()
|
||||
{
|
||||
Title = "Resource not found.",
|
||||
Detail = detail,
|
||||
Type = "https://prefab.dev/problems/not-found",
|
||||
Status = StatusCodes.Status404NotFound,
|
||||
Extensions =
|
||||
{
|
||||
["resource"] = resource,
|
||||
["identifier"] = identifier
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a 409 <see cref="ProblemDetails"/> payload used when a request cannot be completed due to a resource conflict.
|
||||
/// </summary>
|
||||
/// <param name="detail">Human-readable explanation of the conflict.</param>
|
||||
/// <param name="resource">Logical name of the resource involved, when known.</param>
|
||||
/// <param name="identifier">Identifier value supplied by the caller, when known.</param>
|
||||
public static ProblemDetails Conflict(string detail, string? resource = null, string? identifier = null)
|
||||
{
|
||||
var problem = new ProblemDetails
|
||||
{
|
||||
Title = "Request conflict.",
|
||||
Detail = detail,
|
||||
Type = "https://prefab.dev/problems/conflict",
|
||||
Status = StatusCodes.Status409Conflict
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(resource))
|
||||
{
|
||||
problem.Extensions["resource"] = resource;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(identifier))
|
||||
{
|
||||
problem.Extensions["identifier"] = identifier;
|
||||
}
|
||||
|
||||
return problem;
|
||||
}
|
||||
}
|
||||
79
Prefab/Endpoints/EndpointName.cs
Normal file
79
Prefab/Endpoints/EndpointName.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace Prefab.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utility methods for generating standardized endpoint names based on type and module information.
|
||||
/// </summary>
|
||||
/// <remarks>This class is intended for internal use to construct endpoint names that reflect the module and
|
||||
/// feature structure of the application. The generated names are typically used for routing, messaging, or service
|
||||
/// registration scenarios where consistent naming conventions are required. Endpoint names are constructed using the
|
||||
/// namespace or assembly segments of the provided type, followed by the specified endpoint name.</remarks>
|
||||
internal static class EndpointName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default name for the specified type parameter.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type for which to retrieve the default name.</typeparam>
|
||||
/// <returns>A string containing the default name of the type parameter <typeparamref name="T"/>.</returns>
|
||||
public static string For<T>() => For<T>(typeof(T).Name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoint name for the specified type and endpoint identifier.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type for which to retrieve the endpoint name.</typeparam>
|
||||
/// <param name="endpointName">The logical name of the endpoint. Cannot be null or empty.</param>
|
||||
/// <returns>A string representing the endpoint name associated with the specified type and endpoint identifier.</returns>
|
||||
public static string For<T>(string endpointName) => For(typeof(T), endpointName);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a fully qualified endpoint name by combining the module segments derived from the specified type with
|
||||
/// the provided endpoint name.
|
||||
/// </summary>
|
||||
/// <param name="type">The type from which to resolve module segments. Typically, represents the class or module associated with the
|
||||
/// endpoint.</param>
|
||||
/// <param name="endpointName">The name of the endpoint to append. Cannot be null, empty, or consist only of white-space characters.</param>
|
||||
/// <returns>A string representing the fully qualified endpoint name in the format 'Module.Segment.EndpointName'.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if endpointName is null, empty, or consists only of white-space characters.</exception>
|
||||
public static string For(Type type, string endpointName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(endpointName))
|
||||
{
|
||||
throw new ArgumentException("Endpoint name cannot be null or whitespace.", nameof(endpointName));
|
||||
}
|
||||
|
||||
var segments = ResolveModuleSegments(type);
|
||||
return $"{string.Join('.', segments)}.{endpointName}";
|
||||
}
|
||||
|
||||
private static string[] ResolveModuleSegments(MemberInfo type)
|
||||
{
|
||||
if (type is Type concreteType)
|
||||
{
|
||||
var namespaceSegments = ExtractNamespaceSegments(concreteType.Namespace);
|
||||
if (namespaceSegments.Length > 0)
|
||||
{
|
||||
return namespaceSegments;
|
||||
}
|
||||
}
|
||||
|
||||
var assemblySegments = ExtractNamespaceSegments(type.Module?.Assembly?.GetName().Name);
|
||||
return assemblySegments.Length > 0 ? assemblySegments : [type.Name];
|
||||
}
|
||||
|
||||
private static string[] ExtractNamespaceSegments(string? root)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(root))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var segments = root.Split('.', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// Convention: Prefab.<Module>.(App.)<Feature>...
|
||||
return segments
|
||||
.SkipWhile(segment => string.Equals(segment, "Prefab", StringComparison.Ordinal))
|
||||
.Where(segment => !string.Equals(segment, "App", StringComparison.Ordinal))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
29
Prefab/Endpoints/Extensions.cs
Normal file
29
Prefab/Endpoints/Extensions.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace Prefab.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for configuring endpoint names with module-based naming conventions in ASP.NET Core
|
||||
/// routing.
|
||||
/// </summary>
|
||||
/// <remarks>These methods help standardize endpoint naming by prefixing endpoint names with a module name
|
||||
/// inferred from a specified type. This can improve discoverability and organization of endpoints, especially in
|
||||
/// modular applications. The extensions are intended for use with ASP.NET Core's routing infrastructure and are
|
||||
/// typically called when configuring endpoints in minimal APIs or similar scenarios.</remarks>
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies an endpoint name that combines the module name inferred from <typeparamref name="T"/> with the type name.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A type that belongs to the module the endpoint lives in, typically the endpoint class itself.</typeparam>
|
||||
public static RouteHandlerBuilder WithModuleName<T>(this RouteHandlerBuilder builder)
|
||||
=> builder.WithModuleName<T>(typeof(T).Name);
|
||||
|
||||
/// <summary>
|
||||
/// Applies an endpoint name that combines the module name inferred from <typeparamref name="T"/> with the provided endpoint name.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A type that belongs to the module the endpoint lives in, typically the endpoint class itself.</typeparam>
|
||||
/// <param name="endpointName">The base endpoint name that will be prefixed with the module name.</param>
|
||||
public static RouteHandlerBuilder WithModuleName<T>(this RouteHandlerBuilder builder, string endpointName)
|
||||
=> builder.WithName(EndpointName.For<T>(endpointName));
|
||||
}
|
||||
17
Prefab/Endpoints/IEndpointRegistrar.cs
Normal file
17
Prefab/Endpoints/IEndpointRegistrar.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Prefab.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a contract for registering application endpoints with an endpoint route builder.
|
||||
/// </summary>
|
||||
/// <remarks>Implement this interface to configure and map endpoints, such as HTTP routes, to the application's
|
||||
/// routing system. This is typically used during application startup to organize endpoint registration logic.</remarks>
|
||||
public interface IEndpointRegistrar
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures application endpoints using the specified endpoint route builder.
|
||||
/// </summary>
|
||||
/// <param name="endpoints">The endpoint route builder used to map routes for the application. Cannot be null.</param>
|
||||
void MapEndpoints(IEndpointRouteBuilder endpoints);
|
||||
}
|
||||
Reference in New Issue
Block a user