Skip to content

Commit

Permalink
Refactor API controllers and improve response handling
Browse files Browse the repository at this point in the history
  • Loading branch information
KrzysztofPajak committed Jan 2, 2025
1 parent a543137 commit f30f9b5
Show file tree
Hide file tree
Showing 27 changed files with 195 additions and 247 deletions.
10 changes: 5 additions & 5 deletions src/Modules/Grand.Module.Api/Attributes/EnableQueryAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Mvc;
using System.Linq.Dynamic.Core;
using Microsoft.AspNetCore.Http;
using Grand.Module.Api.Constants;

namespace Grand.Module.Api.Attributes;

Expand Down Expand Up @@ -35,13 +36,12 @@ private IQueryable ApplyQueryOptions(IQueryable queryable, IQueryCollection quer
queryable = queryable.Skip(skip);

if (query.TryGetValue("$top", out var topValue) && int.TryParse(topValue, out var top))
queryable = queryable.Take(top);

if (query.TryGetValue("$count", out var countValue) && bool.TryParse(countValue, out var includeCount) && includeCount)
{
var totalCount = queryable.Count();
response?.Headers?.Append("X-Total-Count", totalCount.ToString());
top = Math.Min(top, Configurations.MaxLimit);
queryable = queryable.Take(top);
}
else
queryable = queryable.Take(Configurations.MaxLimit);

return queryable;
}
Expand Down
3 changes: 3 additions & 0 deletions src/Modules/Grand.Module.Api/Controllers/BaseApiController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Grand.Module.Api.Attributes;
using Grand.Module.Api.Constants;
using Grand.Module.Api.Filters;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
Expand All @@ -9,6 +10,8 @@ namespace Grand.Module.Api.Controllers;
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[AuthorizeApiAdmin]
[ServiceFilter(typeof(ModelValidationAttribute))]
[Route($"{Configurations.RestRoutePrefix}/[controller]")]
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
[Produces("application/json")]
public abstract class BaseApiController : ControllerBase
{
Expand Down
9 changes: 3 additions & 6 deletions src/Modules/Grand.Module.Api/Controllers/BrandController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Net;
using Grand.Module.Api.Constants;
using Microsoft.AspNetCore.Http;
using Grand.Module.Api.Attributes;

namespace Grand.Module.Api.Controllers;

[Route($"{Configurations.RestRoutePrefix}/Brand")]
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
public class BrandController : BaseApiController
{
private readonly IMediator _mediator;
Expand Down Expand Up @@ -44,7 +41,7 @@ public async Task<IActionResult> Get()
[HttpGet("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BrandDto))]
public async Task<IActionResult> GetById([FromRoute] string key)
{
if (!await _permissionService.Authorize(PermissionSystemName.Brands)) return Forbid();
Expand All @@ -58,7 +55,7 @@ public async Task<IActionResult> GetById([FromRoute] string key)
[SwaggerOperation("Add new entity to Brand", OperationId = "InsertBrand")]
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BrandDto))]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Post([FromBody] BrandDto model)
{
Expand All @@ -71,7 +68,7 @@ public async Task<IActionResult> Post([FromBody] BrandDto model)
[SwaggerOperation("Update entity in Brand", OperationId = "UpdateBrand")]
[HttpPut("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BrandDto))]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Put([FromRoute] string key, [FromBody] BrandDto model)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Net;
using Grand.Module.Api.Constants;
using Grand.Module.Api.Attributes;
using Microsoft.AspNetCore.Http;

namespace Grand.Module.Api.Controllers;

[Route($"{Configurations.RestRoutePrefix}/BrandLayout")]
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
public class BrandLayoutController : BaseApiController
{
private readonly IMediator _mediator;
Expand All @@ -28,9 +26,9 @@ public BrandLayoutController(IMediator mediator, IPermissionService permissionSe
[SwaggerOperation("Get entity from BrandLayout by key", OperationId = "GetBrandLayoutById")]
[HttpGet("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(LayoutDto))]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Get(string key)
public async Task<IActionResult> Get([FromRoute] string key)
{
if (!await _permissionService.Authorize(PermissionSystemName.Maintenance)) return Forbid();

Expand All @@ -44,7 +42,7 @@ public async Task<IActionResult> Get(string key)
[HttpGet]
[EnableQuery]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<LayoutDto>))]
public async Task<IActionResult> Get()
{
if (!await _permissionService.Authorize(PermissionSystemName.Maintenance)) return Forbid();
Expand Down
22 changes: 10 additions & 12 deletions src/Modules/Grand.Module.Api/Controllers/CategoryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Net;
using Grand.Module.Api.Constants;
using Grand.Module.Api.Attributes;
using Microsoft.AspNetCore.Http;

namespace Grand.Module.Api.Controllers;

[Route($"{Configurations.RestRoutePrefix}/Category")]
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
public class CategoryController : BaseApiController
{
private readonly IMediator _mediator;
Expand All @@ -32,7 +30,7 @@ public CategoryController(
[SwaggerOperation("Get entity from Category by key", OperationId = "GetCategoryById")]
[HttpGet("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CategoryDto))]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Get([FromRoute] string key)
{
Expand All @@ -48,7 +46,7 @@ public async Task<IActionResult> Get([FromRoute] string key)
[HttpGet]
[EnableQuery]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<CategoryDto>))]
public async Task<IActionResult> Get()
{
if (!await _permissionService.Authorize(PermissionSystemName.Categories)) return Forbid();
Expand All @@ -59,7 +57,7 @@ public async Task<IActionResult> Get()
[SwaggerOperation("Add new entity to Category", OperationId = "InsertCategory")]
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CategoryDto))]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Post([FromBody] CategoryDto model)
{
Expand All @@ -70,16 +68,16 @@ public async Task<IActionResult> Post([FromBody] CategoryDto model)
}

[SwaggerOperation("Update entity in Category", OperationId = "UpdateCategory")]
[HttpPut]
[HttpPut("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CategoryDto))]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Put([FromBody] CategoryDto model)
public async Task<IActionResult> Put([FromRoute] string key, [FromBody] CategoryDto model)
{
if (!await _permissionService.Authorize(PermissionSystemName.Categories)) return Forbid();

var category = await _mediator.Send(new GetGenericQuery<CategoryDto, Category>(model.Id));
var category = await _mediator.Send(new GetGenericQuery<CategoryDto, Category>(key));
if (!category.Any()) return NotFound();

model = await _mediator.Send(new UpdateCategoryCommand { Model = model });
Expand Down Expand Up @@ -109,11 +107,11 @@ public async Task<IActionResult> Patch([FromRoute] string key, [FromBody] JsonPa
}

[SwaggerOperation("Delete entity from Category", OperationId = "DeleteCategory")]
[HttpDelete]
[HttpDelete("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Delete(string key)
public async Task<IActionResult> Delete([FromRoute] string key)
{
if (!await _permissionService.Authorize(PermissionSystemName.Categories)) return Forbid();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Net;
using Grand.Module.Api.Constants;
using Grand.Module.Api.Attributes;
using Microsoft.AspNetCore.Http;

namespace Grand.Module.Api.Controllers;

[Route($"{Configurations.RestRoutePrefix}/CategoryLayout")]
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
public class CategoryLayoutController : BaseApiController
{
private readonly IMediator _mediator;
Expand All @@ -30,7 +28,7 @@ public CategoryLayoutController(
[SwaggerOperation("Get entity from CategoryLayout by key", OperationId = "GetCategoryLayoutById")]
[HttpGet("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(LayoutDto))]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Get([FromRoute] string key)
{
Expand All @@ -46,7 +44,7 @@ public async Task<IActionResult> Get([FromRoute] string key)
[HttpGet]
[EnableQuery]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<LayoutDto>))]
public async Task<IActionResult> Get()
{
if (!await _permissionService.Authorize(PermissionSystemName.Maintenance)) return Forbid();
Expand Down
22 changes: 10 additions & 12 deletions src/Modules/Grand.Module.Api/Controllers/CollectionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Grand.Domain.Catalog;
using Grand.Domain.Permissions;
using Grand.Module.Api.Commands.Models.Catalog;
using Grand.Module.Api.Constants;
using Grand.Module.Api.DTOs.Catalog;
using Grand.Module.Api.Attributes;
using Grand.Module.Api.Queries.Models.Common;
Expand All @@ -11,11 +10,10 @@
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Net;
using Microsoft.AspNetCore.Http;

namespace Grand.Module.Api.Controllers;

[Route($"{Configurations.RestRoutePrefix}/Collection")]
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
public class CollectionController : BaseApiController
{
private readonly IMediator _mediator;
Expand All @@ -30,7 +28,7 @@ public CollectionController(IMediator mediator, IPermissionService permissionSer
[SwaggerOperation("Get entity from Collection by key", OperationId = "GetCollectionById")]
[HttpGet("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CollectionDto))]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Get([FromRoute] string key)
{
Expand All @@ -46,7 +44,7 @@ public async Task<IActionResult> Get([FromRoute] string key)
[HttpGet]
[EnableQuery]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<CollectionDto>))]
public async Task<IActionResult> Get()
{
if (!await _permissionService.Authorize(PermissionSystemName.Collections)) return Forbid();
Expand All @@ -57,7 +55,7 @@ public async Task<IActionResult> Get()
[SwaggerOperation("Add new entity to Collection", OperationId = "InsertCollection")]
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CollectionDto))]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Post([FromBody] CollectionDto model)
{
Expand All @@ -68,16 +66,16 @@ public async Task<IActionResult> Post([FromBody] CollectionDto model)
}

[SwaggerOperation("Update entity in Collection", OperationId = "UpdateCollection")]
[HttpPut]
[HttpPut("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CollectionDto))]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Put([FromBody] CollectionDto model)
public async Task<IActionResult> Put([FromRoute] string key, [FromBody] CollectionDto model)
{
if (!await _permissionService.Authorize(PermissionSystemName.Collections)) return Forbid();

var collection = await _mediator.Send(new GetGenericQuery<CollectionDto, Collection>(model.Id));
var collection = await _mediator.Send(new GetGenericQuery<CollectionDto, Collection>(key));
if (!collection.Any()) return NotFound();

model = await _mediator.Send(new UpdateCollectionCommand { Model = model });
Expand Down Expand Up @@ -107,11 +105,11 @@ public async Task<IActionResult> Patch([FromRoute] string key, [FromBody] JsonPa
}

[SwaggerOperation("Delete entity in Collection", OperationId = "DeleteCollection")]
[HttpDelete]
[HttpDelete("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Delete(string key)
public async Task<IActionResult> Delete([FromRoute] string key)
{
if (!await _permissionService.Authorize(PermissionSystemName.Collections)) return Forbid();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Net;
using Grand.Module.Api.Constants;
using Grand.Module.Api.Attributes;
using Microsoft.AspNetCore.Http;

namespace Grand.Module.Api.Controllers;

[Route($"{Configurations.RestRoutePrefix}/CollectionLayout")]
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
public class CollectionLayoutController : BaseApiController
{
private readonly IMediator _mediator;
Expand All @@ -28,7 +26,7 @@ public CollectionLayoutController(IMediator mediator, IPermissionService permiss
[SwaggerOperation("Get entity from CollectionLayout by key", OperationId = "GetCollectionLayoutById")]
[HttpGet("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(LayoutDto))]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Get([FromRoute] string key)
{
Expand All @@ -44,7 +42,7 @@ public async Task<IActionResult> Get([FromRoute] string key)
[HttpGet]
[EnableQuery]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<LayoutDto>))]
public async Task<IActionResult> Get()
{
if (!await _permissionService.Authorize(PermissionSystemName.Maintenance)) return Forbid();
Expand Down
8 changes: 3 additions & 5 deletions src/Modules/Grand.Module.Api/Controllers/CountryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Net;
using Grand.Module.Api.Constants;
using Grand.Module.Api.Attributes;
using Microsoft.AspNetCore.Http;

namespace Grand.Module.Api.Controllers;

[Route($"{Configurations.RestRoutePrefix}/Country")]
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
public class CountryController : BaseApiController
{
private readonly IMediator _mediator;
Expand All @@ -28,7 +26,7 @@ public CountryController(IMediator mediator, IPermissionService permissionServic
[SwaggerOperation("Get entity from Country by key", OperationId = "GetCountryById")]
[HttpGet("{key}")]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CountryDto))]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Get([FromRoute] string key)
{
Expand All @@ -45,7 +43,7 @@ public async Task<IActionResult> Get([FromRoute] string key)
[HttpGet]
[EnableQuery]
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<CountryDto>))]
public async Task<IActionResult> Get()
{
if (!await _permissionService.Authorize(PermissionSystemName.Countries)) return Forbid();
Expand Down
Loading

0 comments on commit f30f9b5

Please sign in to comment.