Skip to content

Commit a543137

Browse files
Refactor
1 parent 68790b9 commit a543137

35 files changed

+241
-753
lines changed

Directory.Packages.props

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,12 @@
3838
<PackageVersion Include="FluentValidation" Version="11.11.0" />
3939
<PackageVersion Include="Microsoft.AspNetCore.JsonPatch" Version="9.0.0" />
4040
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.0" />
41-
<PackageVersion Include="Microsoft.AspNetCore.OData" Version="9.1.1" />
42-
<PackageVersion Include="MongoDB.AspNetCore.OData" Version="1.1.0" />
4341
<PackageVersion Include="Swashbuckle.AspNetCore" Version="7.2.0" />
4442
<PackageVersion Include="Swashbuckle.AspNetCore.Annotations" Version="7.2.0" />
4543
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Facebook" Version="9.0.0" />
4644
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.0" />
4745
<PackageVersion Include="Braintree" Version="5.28.0" />
46+
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.5.1" />
4847
<PackageVersion Include="System.Xml.XPath.XmlDocument" Version="4.7.0" />
4948
<PackageVersion Include="Stripe.net" Version="47.1.0" />
5049
<PackageVersion Include="elFinder.Net.AspNetCore" Version="1.5.0" />
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Microsoft.AspNetCore.Mvc.Filters;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System.Linq.Dynamic.Core;
4+
using Microsoft.AspNetCore.Http;
5+
6+
namespace Grand.Module.Api.Attributes;
7+
8+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
9+
public class EnableQueryAttribute : ActionFilterAttribute
10+
{
11+
public override void OnActionExecuted(ActionExecutedContext context)
12+
{
13+
if (context.Result is not ObjectResult result || result.Value == null)
14+
return;
15+
16+
if (result.Value is IQueryable queryable)
17+
{
18+
queryable = ApplyQueryOptions(queryable, context.HttpContext.Request.Query, context.HttpContext.Response);
19+
result.Value = queryable;
20+
}
21+
}
22+
23+
private IQueryable ApplyQueryOptions(IQueryable queryable, IQueryCollection query, HttpResponse response)
24+
{
25+
if (query.TryGetValue("$filter", out var filter))
26+
queryable = queryable.Where(filter.ToString());
27+
28+
if (query.TryGetValue("$orderby", out var orderBy))
29+
queryable = queryable.OrderBy(orderBy.ToString());
30+
31+
if (query.TryGetValue("$select", out var select))
32+
queryable = queryable.Select($"new({select})");
33+
34+
if (query.TryGetValue("$skip", out var skipValue) && int.TryParse(skipValue, out var skip))
35+
queryable = queryable.Skip(skip);
36+
37+
if (query.TryGetValue("$top", out var topValue) && int.TryParse(topValue, out var top))
38+
queryable = queryable.Take(top);
39+
40+
if (query.TryGetValue("$count", out var countValue) && bool.TryParse(countValue, out var includeCount) && includeCount)
41+
{
42+
var totalCount = queryable.Count();
43+
response?.Headers?.Append("X-Total-Count", totalCount.ToString());
44+
}
45+
46+
return queryable;
47+
}
48+
}

src/Modules/Grand.Module.Api/Infrastructure/ModelValidationAttribute.cs renamed to src/Modules/Grand.Module.Api/Attributes/ModelValidationAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Microsoft.AspNetCore.Mvc;
22
using Microsoft.AspNetCore.Mvc.Filters;
33

4-
namespace Grand.Module.Api.Infrastructure;
4+
namespace Grand.Module.Api.Attributes;
55

66
public class ModelValidationAttribute : ActionFilterAttribute
77
{

src/Modules/Grand.Module.Api/Constants/Configurations.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
public static class Configurations
44
{
5-
public const string ODataRoutePrefix = "odata";
6-
public const string ODataModelBuilderNamespace = "Default";
5+
public const string RestRoutePrefix = "api";
76
public const string CorsPolicyName = "CorsPolicy";
87
public const int MaxLimit = 100;
98
}

src/Modules/Grand.Module.Api/Controllers/BaseODataController.cs renamed to src/Modules/Grand.Module.Api/Controllers/BaseApiController.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
using Grand.Module.Api.Filters;
2-
using Grand.Module.Api.Infrastructure;
1+
using Grand.Module.Api.Attributes;
2+
using Grand.Module.Api.Filters;
33
using Microsoft.AspNetCore.Authentication.JwtBearer;
44
using Microsoft.AspNetCore.Authorization;
55
using Microsoft.AspNetCore.Mvc;
6-
using Microsoft.AspNetCore.OData.Routing.Attributes;
7-
using Microsoft.AspNetCore.OData.Routing.Controllers;
86

97
namespace Grand.Module.Api.Controllers;
108

119
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
12-
[ODataRouteComponent]
1310
[AuthorizeApiAdmin]
1411
[ServiceFilter(typeof(ModelValidationAttribute))]
15-
public abstract class BaseODataController : ODataController
12+
[Produces("application/json")]
13+
public abstract class BaseApiController : ControllerBase
1614
{
1715
public override ForbidResult Forbid()
1816
{

src/Modules/Grand.Module.Api/Controllers/OData/BrandController.cs renamed to src/Modules/Grand.Module.Api/Controllers/BrandController.cs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@
77
using MediatR;
88
using Microsoft.AspNetCore.JsonPatch;
99
using Microsoft.AspNetCore.Mvc;
10-
using MongoDB.AspNetCore.OData;
1110
using Swashbuckle.AspNetCore.Annotations;
1211
using System.Net;
1312
using Grand.Module.Api.Constants;
13+
using Microsoft.AspNetCore.Http;
14+
using Grand.Module.Api.Attributes;
1415

15-
namespace Grand.Module.Api.Controllers.OData;
16+
namespace Grand.Module.Api.Controllers;
1617

17-
[Route($"{Configurations.ODataRoutePrefix}/Brand")]
18+
[Route($"{Configurations.RestRoutePrefix}/Brand")]
1819
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
19-
public class BrandController : BaseODataController
20+
public class BrandController : BaseApiController
2021
{
2122
private readonly IMediator _mediator;
2223
private readonly IPermissionService _permissionService;
@@ -29,9 +30,9 @@ public BrandController(IMediator mediator, IPermissionService permissionService)
2930

3031
[SwaggerOperation("Get entities from Brand", OperationId = "GetBrands")]
3132
[HttpGet]
32-
[MongoEnableQuery]
33+
[EnableQuery]
3334
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
34-
[ProducesResponseType((int)HttpStatusCode.OK)]
35+
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<BrandDto>))]
3536
public async Task<IActionResult> Get()
3637
{
3738
if (!await _permissionService.Authorize(PermissionSystemName.Brands)) return Forbid();
@@ -68,16 +69,16 @@ public async Task<IActionResult> Post([FromBody] BrandDto model)
6869
}
6970

7071
[SwaggerOperation("Update entity in Brand", OperationId = "UpdateBrand")]
71-
[HttpPut]
72+
[HttpPut("{key}")]
7273
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
7374
[ProducesResponseType((int)HttpStatusCode.OK)]
7475
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
7576
[ProducesResponseType((int)HttpStatusCode.NotFound)]
76-
public async Task<IActionResult> Put([FromBody] BrandDto model)
77+
public async Task<IActionResult> Put([FromRoute] string key, [FromBody] BrandDto model)
7778
{
7879
if (!await _permissionService.Authorize(PermissionSystemName.Brands)) return Forbid();
7980

80-
var brand = await _mediator.Send(new GetGenericQuery<BrandDto, Brand>(model.Id));
81+
var brand = await _mediator.Send(new GetGenericQuery<BrandDto, Brand>(key));
8182
if (!brand.Any()) return NotFound();
8283

8384
model = await _mediator.Send(new UpdateBrandCommand { Model = model });
@@ -90,6 +91,13 @@ public async Task<IActionResult> Put([FromBody] BrandDto model)
9091
[ProducesResponseType((int)HttpStatusCode.OK)]
9192
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
9293
[ProducesResponseType((int)HttpStatusCode.NotFound)]
94+
95+
///sample
96+
///{
97+
/// "op": "replace",
98+
/// "path": "/name",
99+
/// "value": "new name"
100+
///}
93101
public async Task<IActionResult> Patch([FromRoute] string key, [FromBody] JsonPatchDocument<BrandDto> model)
94102
{
95103
if (string.IsNullOrEmpty(key))
@@ -107,11 +115,11 @@ public async Task<IActionResult> Patch([FromRoute] string key, [FromBody] JsonPa
107115
}
108116

109117
[SwaggerOperation("Delete entity in Brand", OperationId = "DeleteBrand")]
110-
[HttpDelete]
118+
[HttpDelete("{key}")]
111119
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
112120
[ProducesResponseType((int)HttpStatusCode.OK)]
113121
[ProducesResponseType((int)HttpStatusCode.NotFound)]
114-
public async Task<IActionResult> Delete(string key)
122+
public async Task<IActionResult> Delete([FromRoute] string key)
115123
{
116124
if (!await _permissionService.Authorize(PermissionSystemName.Brands)) return Forbid();
117125

src/Modules/Grand.Module.Api/Controllers/OData/BrandLayoutController.cs renamed to src/Modules/Grand.Module.Api/Controllers/BrandLayoutController.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
using Grand.Domain.Catalog;
66
using MediatR;
77
using Microsoft.AspNetCore.Mvc;
8-
using Microsoft.AspNetCore.OData.Query;
9-
using MongoDB.AspNetCore.OData;
108
using Swashbuckle.AspNetCore.Annotations;
119
using System.Net;
1210
using Grand.Module.Api.Constants;
11+
using Grand.Module.Api.Attributes;
1312

14-
namespace Grand.Module.Api.Controllers.OData;
13+
namespace Grand.Module.Api.Controllers;
1514

16-
[Route($"{Configurations.ODataRoutePrefix}/BrandLayout")]
15+
[Route($"{Configurations.RestRoutePrefix}/BrandLayout")]
1716
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
18-
public class BrandLayoutController : BaseODataController
17+
public class BrandLayoutController : BaseApiController
1918
{
2019
private readonly IMediator _mediator;
2120
private readonly IPermissionService _permissionService;
@@ -43,7 +42,7 @@ public async Task<IActionResult> Get(string key)
4342

4443
[SwaggerOperation("Get entities from BrandLayout", OperationId = "GetBrandLayouts")]
4544
[HttpGet]
46-
[MongoEnableQuery(HandleNullPropagation = HandleNullPropagationOption.False)]
45+
[EnableQuery]
4746
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
4847
[ProducesResponseType((int)HttpStatusCode.OK)]
4948
public async Task<IActionResult> Get()

src/Modules/Grand.Module.Api/Controllers/OData/CategoryController.cs renamed to src/Modules/Grand.Module.Api/Controllers/CategoryController.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@
77
using MediatR;
88
using Microsoft.AspNetCore.JsonPatch;
99
using Microsoft.AspNetCore.Mvc;
10-
using Microsoft.AspNetCore.OData.Query;
11-
using MongoDB.AspNetCore.OData;
1210
using Swashbuckle.AspNetCore.Annotations;
1311
using System.Net;
1412
using Grand.Module.Api.Constants;
13+
using Grand.Module.Api.Attributes;
1514

16-
namespace Grand.Module.Api.Controllers.OData;
15+
namespace Grand.Module.Api.Controllers;
1716

18-
[Route($"{Configurations.ODataRoutePrefix}/Category")]
17+
[Route($"{Configurations.RestRoutePrefix}/Category")]
1918
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
20-
public class CategoryController : BaseODataController
19+
public class CategoryController : BaseApiController
2120
{
2221
private readonly IMediator _mediator;
2322
private readonly IPermissionService _permissionService;
@@ -47,7 +46,7 @@ public async Task<IActionResult> Get([FromRoute] string key)
4746

4847
[SwaggerOperation("Get entities from Category", OperationId = "GetCategories")]
4948
[HttpGet]
50-
[MongoEnableQuery(HandleNullPropagation = HandleNullPropagationOption.False)]
49+
[EnableQuery]
5150
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
5251
[ProducesResponseType((int)HttpStatusCode.OK)]
5352
public async Task<IActionResult> Get()

src/Modules/Grand.Module.Api/Controllers/OData/CategoryLayoutController.cs renamed to src/Modules/Grand.Module.Api/Controllers/CategoryLayoutController.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
using Grand.Domain.Catalog;
66
using MediatR;
77
using Microsoft.AspNetCore.Mvc;
8-
using Microsoft.AspNetCore.OData.Query;
9-
using MongoDB.AspNetCore.OData;
108
using Swashbuckle.AspNetCore.Annotations;
119
using System.Net;
1210
using Grand.Module.Api.Constants;
11+
using Grand.Module.Api.Attributes;
1312

14-
namespace Grand.Module.Api.Controllers.OData;
13+
namespace Grand.Module.Api.Controllers;
1514

16-
[Route($"{Configurations.ODataRoutePrefix}/CategoryLayout")]
15+
[Route($"{Configurations.RestRoutePrefix}/CategoryLayout")]
1716
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
18-
public class CategoryLayoutController : BaseODataController
17+
public class CategoryLayoutController : BaseApiController
1918
{
2019
private readonly IMediator _mediator;
2120
private readonly IPermissionService _permissionService;
@@ -45,7 +44,7 @@ public async Task<IActionResult> Get([FromRoute] string key)
4544

4645
[SwaggerOperation("Get entities from CategoryLayout", OperationId = "GetCategoryLayout")]
4746
[HttpGet]
48-
[MongoEnableQuery(HandleNullPropagation = HandleNullPropagationOption.False)]
47+
[EnableQuery]
4948
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
5049
[ProducesResponseType((int)HttpStatusCode.OK)]
5150
public async Task<IActionResult> Get()

src/Modules/Grand.Module.Api/Controllers/OData/CollectionController.cs renamed to src/Modules/Grand.Module.Api/Controllers/CollectionController.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,19 @@
44
using Grand.Module.Api.Commands.Models.Catalog;
55
using Grand.Module.Api.Constants;
66
using Grand.Module.Api.DTOs.Catalog;
7+
using Grand.Module.Api.Attributes;
78
using Grand.Module.Api.Queries.Models.Common;
89
using MediatR;
910
using Microsoft.AspNetCore.JsonPatch;
1011
using Microsoft.AspNetCore.Mvc;
11-
using Microsoft.AspNetCore.OData.Query;
12-
using MongoDB.AspNetCore.OData;
1312
using Swashbuckle.AspNetCore.Annotations;
1413
using System.Net;
1514

16-
namespace Grand.Module.Api.Controllers.OData;
15+
namespace Grand.Module.Api.Controllers;
1716

18-
[Route($"{Configurations.ODataRoutePrefix}/Collection")]
17+
[Route($"{Configurations.RestRoutePrefix}/Collection")]
1918
[ApiExplorerSettings(IgnoreApi = false, GroupName = "v1")]
20-
public class CollectionController : BaseODataController
19+
public class CollectionController : BaseApiController
2120
{
2221
private readonly IMediator _mediator;
2322
private readonly IPermissionService _permissionService;
@@ -45,7 +44,7 @@ public async Task<IActionResult> Get([FromRoute] string key)
4544

4645
[SwaggerOperation("Get entities from Collection", OperationId = "GetCollections")]
4746
[HttpGet]
48-
[MongoEnableQuery(HandleNullPropagation = HandleNullPropagationOption.False)]
47+
[EnableQuery]
4948
[ProducesResponseType((int)HttpStatusCode.Forbidden)]
5049
[ProducesResponseType((int)HttpStatusCode.OK)]
5150
public async Task<IActionResult> Get()

0 commit comments

Comments
 (0)