diff --git a/src/applications/mixcore/Controllers/PageContentApiController.cs b/src/applications/mixcore/Controllers/PageContentApiController.cs
index ca70b4f1b..78e2e1f45 100644
--- a/src/applications/mixcore/Controllers/PageContentApiController.cs
+++ b/src/applications/mixcore/Controllers/PageContentApiController.cs
@@ -9,14 +9,26 @@
using Mix.Services.Databases.Lib.Interfaces;
using Mix.Shared.Models;
using Mix.SignalR.Interfaces;
+using System.Net;
+using Swashbuckle.AspNetCore.Annotations;
namespace Mixcore.Controllers
{
+ ///
+ /// API controller for managing page content operations
+ ///
[Route("api/v2/rest/mixcore/page-content")]
+ [ApiController]
+ [Produces("application/json")]
+ [SwaggerTag("Page Content Management")]
public sealed class PageContentApiController : MixQueryApiControllerBase
{
private readonly IMixMetadataService _metadataService;
private readonly IMixDbDataService _mixDbDataService;
+
+ ///
+ /// Constructor for PageContentApiController
+ ///
public PageContentApiController(
IHttpContextAccessor httpContextAccessor,
IConfiguration configuration,
@@ -35,11 +47,94 @@ public PageContentApiController(
_mixDbDataService = mixDbDataService;
}
+ ///
+ /// Retrieves a specific page content by its identifier
+ ///
+ /// The page content identifier
+ /// Cancellation token
+ /// The page content details
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved page content", typeof(PageContentViewModel))]
+ [SwaggerResponse((int)HttpStatusCode.NotFound, "Page content not found")]
protected override async Task GetById(int id, CancellationToken cancellationToken = default)
{
var result = await base.GetById(id, cancellationToken);
await result.LoadDataAsync(_mixDbDataService, _metadataService, new(), CacheService);
return result;
}
+
+ ///
+ /// Gets a paginated list of page content items
+ ///
+ /// Search and pagination parameters
+ /// Paginated list of page content items
+ [HttpGet]
+ [SwaggerOperation(
+ Summary = "Get paginated page content items",
+ Description = "Retrieves a paginated list of page content items based on search criteria",
+ OperationId = "GetPageContent",
+ Tags = new[] { "Page Content" }
+ )]
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved page content items", typeof(PagingResponseModel))]
+ [SwaggerResponse((int)HttpStatusCode.BadRequest, "Invalid request parameters")]
+ public override async Task>> Get([FromQuery] SearchRequestDto request)
+ {
+ return await base.Get(request);
+ }
+
+ ///
+ /// Filters page content items based on criteria
+ ///
+ /// Filter and pagination parameters
+ /// Filtered and paginated list of page content items
+ [HttpPost("filter")]
+ [SwaggerOperation(
+ Summary = "Filter page content items",
+ Description = "Filters page content items based on specified criteria",
+ OperationId = "FilterPageContent",
+ Tags = new[] { "Page Content" }
+ )]
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully filtered page content items", typeof(PagingResponseModel))]
+ [SwaggerResponse((int)HttpStatusCode.BadRequest, "Invalid filter parameters")]
+ public override async Task>> Filter([FromBody] SearchRequestDto request)
+ {
+ return await base.Filter(request);
+ }
+
+ ///
+ /// Gets a specific page content by ID
+ ///
+ /// The page content identifier
+ /// The page content details
+ [HttpGet("{id}")]
+ [SwaggerOperation(
+ Summary = "Get page content by ID",
+ Description = "Retrieves a specific page content by its unique identifier",
+ OperationId = "GetPageContentById",
+ Tags = new[] { "Page Content" }
+ )]
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved page content", typeof(PageContentViewModel))]
+ [SwaggerResponse((int)HttpStatusCode.NotFound, "Page content not found")]
+ public override async Task> GetSingle(int id)
+ {
+ return await base.GetSingle(id);
+ }
+
+ ///
+ /// Gets a default page content template
+ ///
+ /// A default page content object
+ [HttpGet("default")]
+ [SwaggerOperation(
+ Summary = "Get default page content template",
+ Description = "Retrieves a default page content object with initialized values",
+ OperationId = "GetDefaultPageContent",
+ Tags = new[] { "Page Content" }
+ )]
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved default page content", typeof(PageContentViewModel))]
+ [SwaggerResponse((int)HttpStatusCode.BadRequest, "Error creating default page content")]
+ public override async Task> GetDefaultAsync()
+ {
+ return await base.GetDefaultAsync();
+ }
}
}
diff --git a/src/applications/mixcore/Controllers/PostContentApiController.cs b/src/applications/mixcore/Controllers/PostContentApiController.cs
index c5209cd17..13996118d 100644
--- a/src/applications/mixcore/Controllers/PostContentApiController.cs
+++ b/src/applications/mixcore/Controllers/PostContentApiController.cs
@@ -11,11 +11,19 @@
using Mix.Services.Databases.Lib.Interfaces;
using Mix.Shared.Models;
using Mix.SignalR.Interfaces;
+using System.Net;
+using Swashbuckle.AspNetCore.Annotations;
namespace Mixcore.Controllers
{
+ ///
+ /// API controller for managing post content operations
+ ///
[EnableCors(MixCorsPolicies.PublicApis)]
[Route("api/v2/rest/mixcore/post-content")]
+ [ApiController]
+ [Produces("application/json")]
+ [SwaggerTag("Post Content Management")]
public sealed class PostContentApiController : MixQueryApiControllerBase
{
private readonly IMixDbDataService _mixDbDataService;
@@ -23,6 +31,10 @@ public sealed class PostContentApiController : MixQueryApiControllerBase
+ /// Constructor for PostContentApiController
+ ///
public PostContentApiController(
IHttpContextAccessor httpContextAccessor,
IConfiguration configuration,
@@ -45,7 +57,14 @@ public PostContentApiController(
_mixDbDataService = mixDbDataService;
}
-
+ ///
+ /// Searches for post content based on the provided criteria
+ ///
+ /// Search request parameters
+ /// Cancellation token
+ /// Paged list of post content items
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved post content items", typeof(PagingResponseModel))]
+ [SwaggerResponse((int)HttpStatusCode.BadRequest, "Invalid request parameters")]
protected override async Task> SearchHandler(SearchRequestDto req, CancellationToken cancellationToken = default)
{
var searchPostQuery = new SearchPostQueryModel(Request, req, CurrentTenant.Id);
@@ -59,11 +78,94 @@ protected override async Task> SearchH
return RestApiService.ParseSearchResult(req, result);
}
+ ///
+ /// Retrieves a specific post content by its identifier
+ ///
+ /// The post content identifier
+ /// Cancellation token
+ /// The post content details
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved post content", typeof(PostContentViewModel))]
+ [SwaggerResponse((int)HttpStatusCode.NotFound, "Post content not found")]
protected override async Task GetById(int id, CancellationToken cancellationToken = default)
{
var result = await base.GetById(id);
await result.LoadAdditionalDataAsync(_mixDbDataService, _metadataService, CacheService, cancellationToken);
return result;
}
+
+ ///
+ /// Gets a paginated list of post content items
+ ///
+ /// Search and pagination parameters
+ /// Paginated list of post content items
+ [HttpGet]
+ [SwaggerOperation(
+ Summary = "Get paginated post content items",
+ Description = "Retrieves a paginated list of post content items based on search criteria",
+ OperationId = "GetPostContent",
+ Tags = new[] { "Post Content" }
+ )]
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved post content items", typeof(PagingResponseModel))]
+ [SwaggerResponse((int)HttpStatusCode.BadRequest, "Invalid request parameters")]
+ public override async Task>> Get([FromQuery] SearchRequestDto request)
+ {
+ return await base.Get(request);
+ }
+
+ ///
+ /// Filters post content items based on criteria
+ ///
+ /// Filter and pagination parameters
+ /// Filtered and paginated list of post content items
+ [HttpPost("filter")]
+ [SwaggerOperation(
+ Summary = "Filter post content items",
+ Description = "Filters post content items based on specified criteria",
+ OperationId = "FilterPostContent",
+ Tags = new[] { "Post Content" }
+ )]
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully filtered post content items", typeof(PagingResponseModel))]
+ [SwaggerResponse((int)HttpStatusCode.BadRequest, "Invalid filter parameters")]
+ public override async Task>> Filter([FromBody] SearchRequestDto request)
+ {
+ return await base.Filter(request);
+ }
+
+ ///
+ /// Gets a specific post content by ID
+ ///
+ /// The post content identifier
+ /// The post content details
+ [HttpGet("{id}")]
+ [SwaggerOperation(
+ Summary = "Get post content by ID",
+ Description = "Retrieves a specific post content by its unique identifier",
+ OperationId = "GetPostContentById",
+ Tags = new[] { "Post Content" }
+ )]
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved post content", typeof(PostContentViewModel))]
+ [SwaggerResponse((int)HttpStatusCode.NotFound, "Post content not found")]
+ public override async Task> GetSingle(int id)
+ {
+ return await base.GetSingle(id);
+ }
+
+ ///
+ /// Gets a default post content template
+ ///
+ /// A default post content object
+ [HttpGet("default")]
+ [SwaggerOperation(
+ Summary = "Get default post content template",
+ Description = "Retrieves a default post content object with initialized values",
+ OperationId = "GetDefaultPostContent",
+ Tags = new[] { "Post Content" }
+ )]
+ [SwaggerResponse((int)HttpStatusCode.OK, "Successfully retrieved default post content", typeof(PostContentViewModel))]
+ [SwaggerResponse((int)HttpStatusCode.BadRequest, "Error creating default post content")]
+ public override async Task> GetDefaultAsync()
+ {
+ return await base.GetDefaultAsync();
+ }
}
}
diff --git a/src/applications/mixcore/mixcore.csproj b/src/applications/mixcore/mixcore.csproj
index 4dbe2f155..b4fcb40fe 100644
--- a/src/applications/mixcore/mixcore.csproj
+++ b/src/applications/mixcore/mixcore.csproj
@@ -14,6 +14,8 @@
False
False
none
+ true
+ $(NoWarn);1591
diff --git a/src/applications/mixcore/wwwroot/mix-app/css/swagger.css b/src/applications/mixcore/wwwroot/mix-app/css/swagger.css
index b7dcaf7fe..4e59317d9 100644
--- a/src/applications/mixcore/wwwroot/mix-app/css/swagger.css
+++ b/src/applications/mixcore/wwwroot/mix-app/css/swagger.css
@@ -13,17 +13,106 @@
.topbar{
display: none;
}
+
+/* Improve the API endpoint sections */
.swagger-ui .opblock-tag {
font-size: 18px;
- /* color: #3b4151;
- font-family: sans-serif;
- margin: 0 0 5px;
- background: #333;*/
+ margin: 12px 0;
+ padding: 10px;
+ border-radius: 4px;
+ transition: all 0.3s ease;
+}
+
+.swagger-ui .opblock-tag-section {
+ margin-bottom: 16px;
+}
+
+.swagger-ui .opblock {
+ margin: 0 0 15px;
+ border-radius: 6px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+}
+
+/* Improve the GET, POST, PUT, DELETE method coloring */
+.swagger-ui .opblock-get {
+ border-color: #61affe;
+ background: rgba(97, 175, 254, 0.1);
+}
+
+.swagger-ui .opblock-post {
+ border-color: #49cc90;
+ background: rgba(73, 204, 144, 0.1);
+}
+
+.swagger-ui .opblock-put {
+ border-color: #fca130;
+ background: rgba(252, 161, 48, 0.1);
+}
+
+.swagger-ui .opblock-delete {
+ border-color: #f93e3e;
+ background: rgba(249, 62, 62, 0.1);
+}
+
+/* Improve typography */
+.swagger-ui, .swagger-ui .opblock-tag, .swagger-ui .opblock .opblock-summary-operation-id,
+.swagger-ui .opblock .opblock-summary-path, .swagger-ui .opblock-description-wrapper p,
+.swagger-ui .response-col_status, .swagger-ui table thead tr td,
+.swagger-ui table thead tr th, .swagger-ui .parameter__name,
+.swagger-ui .tab {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+}
+
+/* Improve the model schema display */
+.swagger-ui .model-box {
+ padding: 12px;
+ border-radius: 4px;
+ background: rgba(0, 0, 0, 0.02);
+}
+
+.swagger-ui .model-title {
+ font-size: 16px;
+ font-weight: 600;
+}
+
+/* Improve the response section */
+.swagger-ui .responses-table {
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.swagger-ui .response-col_status {
+ font-weight: 600;
+}
+
+/* Add hover effects */
+.swagger-ui .opblock:hover {
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
}
-.opblock-tag-section.is-open h3{
+
+/* Improve tag section when open */
+.opblock-tag-section.is-open h3 {
color: #fff;
background: #333;
+ border-radius: 4px;
+ padding: 10px;
+}
+
+/* Improve the authorize button */
+.swagger-ui .btn.authorize {
+ border-color: #49cc90;
+ color: #49cc90;
+ transition: all 0.3s ease;
}
-.swagger-ui .opblock-tag:hover {
- color: #333;
+
+.swagger-ui .btn.authorize:hover {
+ background-color: #49cc90;
+ color: #fff;
+}
+
+/* Make operation summaries more readable */
+.swagger-ui .opblock .opblock-summary-description {
+ font-size: 14px;
+ color: #3b4151;
+ padding: 0 8px;
}
\ No newline at end of file
diff --git a/src/platform/mix.library/Filters/SwaggerDefaultResponsesFilter.cs b/src/platform/mix.library/Filters/SwaggerDefaultResponsesFilter.cs
new file mode 100644
index 000000000..747d4f912
--- /dev/null
+++ b/src/platform/mix.library/Filters/SwaggerDefaultResponsesFilter.cs
@@ -0,0 +1,145 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+using Mix.Lib.Models.ApiResponse;
+using Swashbuckle.AspNetCore.SwaggerGen;
+using System.Net;
+using System.Reflection;
+
+namespace Mix.Lib.Filters
+{
+ ///
+ /// Swagger operation filter that adds standard responses to all API endpoints
+ ///
+ public class SwaggerDefaultResponsesFilter : IOperationFilter
+ {
+ public void Apply(OpenApiOperation operation, OperationFilterContext context)
+ {
+ // Ensure we have a response dictionary
+ if (operation.Responses == null)
+ {
+ operation.Responses = new OpenApiResponses();
+ }
+
+ // Add common error responses for all API endpoints
+ AddErrorResponse(operation, HttpStatusCode.InternalServerError, "Internal server error occurred");
+ AddErrorResponse(operation, HttpStatusCode.BadRequest, "Invalid request");
+
+ // Add auth-related responses if the endpoint requires authorization
+ if (context.MethodInfo.DeclaringType.GetCustomAttributes(true).Any() ||
+ context.MethodInfo.GetCustomAttributes(true).Any())
+ {
+ AddErrorResponse(operation, HttpStatusCode.Unauthorized, "Unauthorized access");
+ AddErrorResponse(operation, HttpStatusCode.Forbidden, "Access forbidden");
+ }
+
+ // Add example responses when applicable
+ AddResponseExamples(operation, context);
+ }
+
+ private void AddErrorResponse(OpenApiOperation operation, HttpStatusCode statusCode, string description)
+ {
+ string statusCodeString = ((int)statusCode).ToString();
+
+ if (!operation.Responses.ContainsKey(statusCodeString))
+ {
+ var response = new OpenApiResponse
+ {
+ Description = description,
+ Content = new Dictionary
+ {
+ ["application/json"] = new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
+ {
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.Schema,
+ Id = nameof(ApiErrorResponse)
+ }
+ }
+ }
+ }
+ };
+
+ operation.Responses.Add(statusCodeString, response);
+ }
+ }
+
+ private void AddResponseExamples(OpenApiOperation operation, OperationFilterContext context)
+ {
+ // Add example responses for specific status codes
+ foreach (var (key, response) in operation.Responses)
+ {
+ if (int.TryParse(key, out var statusCode))
+ {
+ switch (statusCode)
+ {
+ case 400: // Bad Request
+ AddErrorExample(response, HttpStatusCode.BadRequest, "Validation failed",
+ "One or more validation errors occurred",
+ new Dictionary
+ {
+ ["name"] = new[] { "Name is required" },
+ ["email"] = new[] { "Invalid email format" }
+ });
+ break;
+
+ case 401: // Unauthorized
+ AddErrorExample(response, HttpStatusCode.Unauthorized, "Unauthorized",
+ "Authentication credentials are missing or invalid");
+ break;
+
+ case 403: // Forbidden
+ AddErrorExample(response, HttpStatusCode.Forbidden, "Forbidden",
+ "You don't have permission to access this resource");
+ break;
+
+ case 404: // Not Found
+ AddErrorExample(response, HttpStatusCode.NotFound, "Resource not found",
+ "The requested resource could not be found");
+ break;
+
+ case 500: // Internal Server Error
+ AddErrorExample(response, HttpStatusCode.InternalServerError, "Internal server error",
+ "An unexpected error occurred");
+ break;
+ }
+ }
+ }
+ }
+
+ private void AddErrorExample(OpenApiResponse response, HttpStatusCode statusCode, string message, string details,
+ Dictionary errors = null)
+ {
+ if (response.Content.TryGetValue("application/json", out var mediaType))
+ {
+ var example = new OpenApiObject
+ {
+ ["statusCode"] = new OpenApiInteger((int)statusCode),
+ ["message"] = new OpenApiString(message),
+ ["details"] = new OpenApiString(details),
+ ["timestamp"] = new OpenApiString(DateTime.UtcNow.ToString("o"))
+ };
+
+ // Add errors if provided
+ if (errors != null)
+ {
+ var errorsObj = new OpenApiObject();
+ foreach (var (key, values) in errors)
+ {
+ var valuesArray = new OpenApiArray();
+ foreach (var value in values)
+ {
+ valuesArray.Add(new OpenApiString(value));
+ }
+ errorsObj.Add(key, valuesArray);
+ }
+ example.Add("errors", errorsObj);
+ }
+
+ mediaType.Example = example;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/platform/mix.library/Models/ApiResponse/ApiErrorResponse.cs b/src/platform/mix.library/Models/ApiResponse/ApiErrorResponse.cs
new file mode 100644
index 000000000..417f79d98
--- /dev/null
+++ b/src/platform/mix.library/Models/ApiResponse/ApiErrorResponse.cs
@@ -0,0 +1,120 @@
+using System.Net;
+using System.Text.Json.Serialization;
+
+namespace Mix.Lib.Models.ApiResponse
+{
+ ///
+ /// Standardized error response for API endpoints
+ ///
+ public class ApiErrorResponse
+ {
+ ///
+ /// Error status code
+ ///
+ [JsonPropertyName("statusCode")]
+ public int StatusCode { get; set; }
+
+ ///
+ /// Error message
+ ///
+ [JsonPropertyName("message")]
+ public string Message { get; set; }
+
+ ///
+ /// Detailed error information
+ ///
+ [JsonPropertyName("details")]
+ public string Details { get; set; }
+
+ ///
+ /// Timestamp when the error occurred
+ ///
+ [JsonPropertyName("timestamp")]
+ public DateTime Timestamp { get; set; } = DateTime.UtcNow;
+
+ ///
+ /// Additional error data
+ ///
+ [JsonPropertyName("errors")]
+ public Dictionary Errors { get; set; }
+
+ ///
+ /// Creates a new API error response
+ ///
+ /// HTTP status code
+ /// Error message
+ /// Detailed error information
+ /// Additional validation errors
+ /// The API error response
+ public static ApiErrorResponse Create(
+ HttpStatusCode statusCode,
+ string message,
+ string details = null,
+ Dictionary errors = null)
+ {
+ return new ApiErrorResponse
+ {
+ StatusCode = (int)statusCode,
+ Message = message,
+ Details = details,
+ Errors = errors
+ };
+ }
+
+ ///
+ /// Creates a not found error response
+ ///
+ /// Name of the resource that wasn't found
+ /// ID of the resource that wasn't found
+ /// The API error response
+ public static ApiErrorResponse NotFound(string resourceName, object resourceId)
+ {
+ return Create(
+ HttpStatusCode.NotFound,
+ $"{resourceName} not found",
+ $"The requested {resourceName} with ID {resourceId} could not be found."
+ );
+ }
+
+ ///
+ /// Creates a bad request error response
+ ///
+ /// Error message
+ /// Validation errors
+ /// The API error response
+ public static ApiErrorResponse BadRequest(string message, Dictionary errors = null)
+ {
+ return Create(
+ HttpStatusCode.BadRequest,
+ message,
+ errors: errors
+ );
+ }
+
+ ///
+ /// Creates an unauthorized error response
+ ///
+ /// Error message
+ /// The API error response
+ public static ApiErrorResponse Unauthorized(string message = "Unauthorized access")
+ {
+ return Create(
+ HttpStatusCode.Unauthorized,
+ message
+ );
+ }
+
+ ///
+ /// Creates a forbidden error response
+ ///
+ /// Error message
+ /// The API error response
+ public static ApiErrorResponse Forbidden(string message = "Access forbidden")
+ {
+ return Create(
+ HttpStatusCode.Forbidden,
+ message
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/platform/mix.library/Models/ApiResponse/ApiSuccessResponse.cs b/src/platform/mix.library/Models/ApiResponse/ApiSuccessResponse.cs
new file mode 100644
index 000000000..09786d902
--- /dev/null
+++ b/src/platform/mix.library/Models/ApiResponse/ApiSuccessResponse.cs
@@ -0,0 +1,127 @@
+using System.Net;
+using System.Text.Json.Serialization;
+
+namespace Mix.Lib.Models.ApiResponse
+{
+ ///
+ /// Standardized success response for API endpoints
+ ///
+ /// Type of the data being returned
+ public class ApiSuccessResponse
+ {
+ ///
+ /// Success status code
+ ///
+ [JsonPropertyName("statusCode")]
+ public int StatusCode { get; set; } = (int)HttpStatusCode.OK;
+
+ ///
+ /// Success message
+ ///
+ [JsonPropertyName("message")]
+ public string Message { get; set; } = "Operation completed successfully";
+
+ ///
+ /// Timestamp when the response was created
+ ///
+ [JsonPropertyName("timestamp")]
+ public DateTime Timestamp { get; set; } = DateTime.UtcNow;
+
+ ///
+ /// The response data
+ ///
+ [JsonPropertyName("data")]
+ public T Data { get; set; }
+
+ ///
+ /// Creates a new API success response
+ ///
+ /// Response data
+ /// HTTP status code
+ /// Success message
+ /// The API success response
+ public static ApiSuccessResponse Create(T data, HttpStatusCode statusCode = HttpStatusCode.OK, string message = null)
+ {
+ return new ApiSuccessResponse
+ {
+ StatusCode = (int)statusCode,
+ Message = message ?? "Operation completed successfully",
+ Data = data
+ };
+ }
+
+ ///
+ /// Creates an OK response
+ ///
+ /// Response data
+ /// Success message
+ /// The API success response
+ public static ApiSuccessResponse Ok(T data, string message = "Operation completed successfully")
+ {
+ return Create(data, HttpStatusCode.OK, message);
+ }
+
+ ///
+ /// Creates a Created response
+ ///
+ /// Created entity data
+ /// Success message
+ /// The API success response
+ public static ApiSuccessResponse Created(T data, string message = "Resource created successfully")
+ {
+ return Create(data, HttpStatusCode.Created, message);
+ }
+
+ ///
+ /// Creates a No Content response
+ ///
+ /// Success message
+ /// The API success response
+ public static ApiSuccessResponse NoContent(string message = "Operation completed successfully with no content")
+ {
+ return Create(default, HttpStatusCode.NoContent, message);
+ }
+ }
+
+ ///
+ /// Non-generic success response for endpoints that don't return data
+ ///
+ public class ApiSuccessResponse : ApiSuccessResponse