erp-platform/api/src/Kurs.Platform.Application/CustomEndpoints/CustomEndpointAppService.cs

285 lines
11 KiB
C#
Raw Normal View History

2025-05-06 06:45:49 +00:00
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Kurs.Platform.DynamicData;
using Kurs.Platform.Entities;
using Kurs.Platform.Extensions;
using Kurs.Platform.Queries;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Tracing;
using Volo.Abp.Uow;
2025-07-27 18:57:19 +00:00
namespace Kurs.Platform.CustomEndpoints;
2025-05-06 06:45:49 +00:00
[Authorize]
2025-07-27 18:57:19 +00:00
[Route("api/app/custom-endpoint")]
public class CustomEndpointAppService : PlatformAppService
2025-05-06 06:45:49 +00:00
{
2025-07-27 18:57:19 +00:00
private readonly IRepository<CustomEndpoint, Guid> repo;
2025-05-06 06:45:49 +00:00
private readonly IHttpContextAccessor httpContextAccessor;
private readonly IDataSourceManager dataSourceManager;
private readonly IDynamicDataManager dynamicDataManager;
2025-07-27 18:57:19 +00:00
public CustomEndpointAppService(
IRepository<CustomEndpoint, Guid> repo,
2025-05-06 06:45:49 +00:00
IHttpContextAccessor httpContextAccessor,
IDataSourceManager dataSourceManager,
IDynamicDataManager dynamicDataManager)
{
this.repo = repo;
this.httpContextAccessor = httpContextAccessor;
this.dataSourceManager = dataSourceManager;
this.dynamicDataManager = dynamicDataManager;
}
[HttpGet("{**path}")]
2025-07-27 18:57:19 +00:00
[Authorize(PlatformConsts.AppCodes.CustomEndpoints.Get)]
2025-05-06 06:45:49 +00:00
public async Task<IActionResult> GetAsync()
{
return await Execute("GET");
}
[HttpPost("{**path}")]
2025-07-27 18:57:19 +00:00
[Authorize(PlatformConsts.AppCodes.CustomEndpoints.Post)]
2025-05-06 06:45:49 +00:00
public async Task<IActionResult> PostAsync()
{
return await Execute("POST");
}
private async Task<IActionResult> Execute(string method)
{
using var uow = UnitOfWorkManager.Begin(new AbpUnitOfWorkOptions(false), true);
try
{
2025-07-27 20:52:31 +00:00
// Request.Path = /api/app/custom-endpoint/yxcdfn/8
var rawPath = httpContextAccessor.HttpContext.Request.Path.ToString();
var decodedPath = Uri.UnescapeDataString(rawPath); // URL decode işlemi
var path = decodedPath
.Replace("/api/app/custom-endpoint", "", StringComparison.OrdinalIgnoreCase)
2025-05-06 06:45:49 +00:00
.EnsureStartsWith('/')
.EnsureEndsWith('/');
2025-07-27 20:52:31 +00:00
2025-07-27 18:57:19 +00:00
Logger.LogInformation("Custom Endpoint çağrısı. Kullanıcı:{user} Path:[{method}]{path}", CurrentUser.UserName, "GET", path);
2025-05-06 06:45:49 +00:00
var api = await repo.FirstOrDefaultAsync(a => path.StartsWith(a.Url) && a.Method == method);
if (api is null)
{
2025-07-27 18:57:19 +00:00
Logger.LogInformation("Custom Endpoint bulunamadı");
2025-05-06 06:45:49 +00:00
return new NotFoundResult();
}
2025-07-27 18:57:19 +00:00
Logger.LogInformation("Custom Endpoint bulundu. {api}", api.Name);
2025-05-06 06:45:49 +00:00
var canUse = api.Permissions.Any(a =>
(a.ResourceType == "User" && a.ResourceId == CurrentUser.UserName) ||
(a.ResourceType == "Role" && CurrentUser.Roles.Contains(a.ResourceId)) ||
(a.ResourceType == "Global"));
if (!canUse)
{
2025-07-27 18:57:19 +00:00
Logger.LogWarning("Custom Endpoint yetki yok");
2025-05-06 06:45:49 +00:00
return new UnauthorizedResult();
}
Dictionary<string, object> param = [];
// Parametreler:
// 1- Statik
2025-07-27 18:57:19 +00:00
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Static))
2025-05-06 06:45:49 +00:00
{
var value = GetDefaultValue(item.DefaultValue);
param.Add(item.Name, value);
}
// 2- Query
var queryParams = httpContextAccessor.HttpContext.Request.Query;
2025-07-27 18:57:19 +00:00
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Query))
2025-05-06 06:45:49 +00:00
{
if (queryParams.TryGetValue(item.Name, out var value))
{
param.Add(item.Name, value);
}
else
{
if (item.IsRequired)
{
throw new Volo.Abp.UserFriendlyException(L[PlatformConsts.AppErrorCodes.ParameterNotValid, item.Name]);
}
else
{
param.Add(item.Name, GetDefaultValue(item.DefaultValue));
}
}
}
// 3- Path
2025-07-27 18:57:19 +00:00
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Path && !a.Path.IsNullOrWhiteSpace()))
2025-05-06 06:45:49 +00:00
{
var itemPath = item.Path.EnsureStartsWith('/').EnsureEndsWith('/');
var index = itemPath.IndexOf($"/:{item.Name}/");
if (index == -1)
{
throw new Volo.Abp.UserFriendlyException(L[PlatformConsts.AppErrorCodes.ParameterNotValid, item.Name]);
}
var segmentCount = itemPath[..(index + 1)].Count(a => a == '/');
var value = path.GetSegment('/', segmentCount);
param.Add(item.Name, value ?? GetDefaultValue(item.DefaultValue));
}
// 4- Body
if (method == "POST")
{
var body = await httpContextAccessor.HttpContext.Request.ReadFormAsync();
2025-07-27 18:57:19 +00:00
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Body))
2025-05-06 06:45:49 +00:00
{
if (body.TryGetValue(item.Name, out var value))
{
param.Add(item.Name, value);
}
else
{
if (item.IsRequired)
{
throw new Volo.Abp.UserFriendlyException(L[PlatformConsts.AppErrorCodes.ParameterNotValid, item.Name]);
}
else
{
param.Add(item.Name, GetDefaultValue(item.DefaultValue));
}
}
}
}
Logger.LogInformation("Parametreler: {param}", param);
var (dynamicDataRepository, connectionString, _) = await dynamicDataManager.GetAsync(api.DataSourceCode == "!Tenant", api.DataSourceCode);
var result = await dynamicDataRepository.QueryAsync(api.Sql, connectionString, param);
Logger.LogInformation("Sonuç başarılı");
Logger.LogInformation("{result}", result);
await uow.CompleteAsync();
return new ObjectResult(result);
}
catch (Volo.Abp.UserFriendlyException ex)
{
Logger.LogException(ex);
await uow.RollbackAsync();
return new JsonResult(new
{
Message = ex.Message,
CorrelationId = LazyServiceProvider.GetRequiredService<ICorrelationIdProvider>().Get(),
})
{
StatusCode = 500,
};
}
catch (Exception ex)
{
Logger.LogException(ex);
await uow.RollbackAsync();
return new JsonResult(new
{
Message = L[PlatformConsts.AppErrorCodes.InternalError].Value,
CorrelationId = LazyServiceProvider.GetRequiredService<ICorrelationIdProvider>().Get()
})
{
StatusCode = 500,
};
}
}
private string GetDefaultValue(string strValue)
{
return strValue?.Replace(PlatformConsts.DefaultValues.UserId, CurrentUser.Id.ToString())
.Replace(PlatformConsts.DefaultValues.UserName, CurrentUser.UserName)
.Replace(PlatformConsts.DefaultValues.Roles, CurrentUser.Roles.JoinAsString("','"))
.Replace(PlatformConsts.DefaultValues.Now, Clock.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture))
.Replace(PlatformConsts.DefaultValues.TenantId, CurrentTenant.Id.HasValue ? CurrentTenant.Id.ToString() : null);
}
}
2025-07-27 18:57:19 +00:00
//TODO: Custom Endpoint rol, permission seed
2025-05-06 06:45:49 +00:00
/*
Token İsteği Örnek:
POST /connect/token HTTP/1.1
Host: localhost:44344
Content-Type: application/x-www-form-urlencoded
2025-05-06 11:03:45 +00:00
username=system%40sozsoft.com
2025-05-06 06:45:49 +00:00
&password=...
&grant_type=password
&client_id=Platform_PublicApi
&scope=offline_access%20Platform
2025-07-27 18:57:19 +00:00
Custom Endpoint Seed:
2025-07-27 20:52:31 +00:00
SELECT * FROM PLanguage WHERE IsEnabled = @IsEnabled AND CultureName = @CultureName
2025-05-06 06:45:49 +00:00
INSERT INTO [dbo].[Orders]
([CustomerName]
,[ProductName]
,[OrderDate]
,[Quantity])
OUTPUT Inserted.*
VALUES
(@CustomerName, @ProductName, @OrderDate, @Quantity)
SELECT * FROM Orders WHERE Id = SCOPE_IDENTITY()
[{ "Type": "S", "Name": "CultureName", "DefaultValue": "ar" }]
[{ "Type": "Q", "Name": "CultureName", "DefaultValue": "ar" }]
[
{ "Type": "P", "Name": "IsEnabled", "DefaultValue": "en", "Path": "/yxcdfn/8/:IsEnabled/:CultureName/" },
{ "Type": "P", "Name": "CultureName", "DefaultValue": "en", "Path": "/yxcdfn/8/:IsEnabled/:CultureName/" }
]
[
{ "Type": "B", "Name": "CustomerName", "DefaultValue": "" },
{ "Type": "B", "Name": "ProductName", "DefaultValue": "" },
{ "Type": "B", "Name": "OrderDate", "DefaultValue": "@NOW" },
{ "Type": "B", "Name": "Quantity", "DefaultValue": "" }
]
Guid? TenantId
string Name
string Description
2025-08-11 06:34:44 +00:00
string Url -> https://api.sozsoft.com/api/app/dinamik/yxfgu
2025-05-06 06:45:49 +00:00
string Method -> GET
string Params = [
{ Type: 'Static', Name: 'StartDate', DefaultValue: '234' },
{ Type: 'Query', Name: 'StartDate', DefaultValue: '', IsRequired: false },
{ Type: 'Path', Name: 'FaturaId', DefaultValue: '', Path: '/yxfgu/fatura/:FaturaId/kalem/357' },
{ Type: 'Path', Name: 'KalemId', DefaultValue: '', Path: '/yxfgu/fatura/xxx/kalem/:KalemId' },
{ Type: 'Body', Name: 'StartDate', DefaultValue: '' },
]
string Sql -> SELECT * FROM VSatislar WHERE MusteriId = @MusteriId AND StartDate >= @StartDate
string Permissions = [
2025-05-06 11:03:45 +00:00
{ ResourceType: 'User', ResourceId: 'system' },
2025-05-06 06:45:49 +00:00
{ ResourceType: 'User', ResourceId: 'sedat' },
]
Query Parameter
2025-08-11 06:34:44 +00:00
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu
2025-05-06 06:45:49 +00:00
Method: GET
Parameters: ?StartDate=2024-12-31&EndDate=2025-12-31
Path Parameter
2025-08-11 06:34:44 +00:00
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu/fatura/467/kalem/357
2025-05-06 06:45:49 +00:00
Method: GET
Parameters: FaturaId=467
Body Parameter
2025-08-11 06:34:44 +00:00
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu?UrunId=5
2025-05-06 06:45:49 +00:00
Method: POST
Parameters: ?StartDate=2024-12-31&EndDate=2025-12-31
Body: { Tutar: 2000, Tarih: '2024-12-31' }
*/