278 lines
10 KiB
C#
278 lines
10 KiB
C#
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;
|
||
|
||
namespace Kurs.Platform.CustomEndpoints;
|
||
|
||
[Authorize]
|
||
[Route("api/app/custom-endpoint")]
|
||
public class CustomEndpointAppService : PlatformAppService
|
||
{
|
||
private readonly IRepository<CustomEndpoint, Guid> repo;
|
||
private readonly IHttpContextAccessor httpContextAccessor;
|
||
private readonly IDataSourceManager dataSourceManager;
|
||
private readonly IDynamicDataManager dynamicDataManager;
|
||
private readonly DefaultValueHelper defaultValueHelper;
|
||
|
||
public CustomEndpointAppService(
|
||
IRepository<CustomEndpoint, Guid> repo,
|
||
IHttpContextAccessor httpContextAccessor,
|
||
IDataSourceManager dataSourceManager,
|
||
IDynamicDataManager dynamicDataManager,
|
||
DefaultValueHelper defaultValueHelper)
|
||
{
|
||
this.repo = repo;
|
||
this.httpContextAccessor = httpContextAccessor;
|
||
this.dataSourceManager = dataSourceManager;
|
||
this.dynamicDataManager = dynamicDataManager;
|
||
this.defaultValueHelper = defaultValueHelper;
|
||
}
|
||
|
||
[HttpGet("{**path}")]
|
||
[Authorize(PlatformConsts.AppCodes.CustomEndpoints.Get)]
|
||
public async Task<IActionResult> GetAsync()
|
||
{
|
||
return await Execute("GET");
|
||
}
|
||
|
||
[HttpPost("{**path}")]
|
||
[Authorize(PlatformConsts.AppCodes.CustomEndpoints.Post)]
|
||
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
|
||
{
|
||
// 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)
|
||
.EnsureStartsWith('/')
|
||
.EnsureEndsWith('/');
|
||
|
||
Logger.LogInformation("Custom Endpoint çağrısı. Kullanıcı:{user} Path:[{method}]{path}", CurrentUser.UserName, "GET", path);
|
||
var api = await repo.FirstOrDefaultAsync(a => path.StartsWith(a.Url) && a.Method == method);
|
||
if (api is null)
|
||
{
|
||
Logger.LogInformation("Custom Endpoint bulunamadı");
|
||
return new NotFoundResult();
|
||
}
|
||
Logger.LogInformation("Custom Endpoint bulundu. {api}", api.Name);
|
||
|
||
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)
|
||
{
|
||
Logger.LogWarning("Custom Endpoint yetki yok");
|
||
return new UnauthorizedResult();
|
||
}
|
||
|
||
|
||
Dictionary<string, object> param = [];
|
||
// Parametreler:
|
||
// 1- Statik
|
||
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Static))
|
||
{
|
||
var value = defaultValueHelper.GetDefaultValue(item.DefaultValue);
|
||
param.Add(item.Name, value);
|
||
}
|
||
|
||
// 2- Query
|
||
var queryParams = httpContextAccessor.HttpContext.Request.Query;
|
||
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Query))
|
||
{
|
||
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, defaultValueHelper.GetDefaultValue(item.DefaultValue));
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3- Path
|
||
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Path && !a.Path.IsNullOrWhiteSpace()))
|
||
{
|
||
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 ?? defaultValueHelper.GetDefaultValue(item.DefaultValue));
|
||
}
|
||
|
||
// 4- Body
|
||
if (method == "POST")
|
||
{
|
||
var body = await httpContextAccessor.HttpContext.Request.ReadFormAsync();
|
||
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Body))
|
||
{
|
||
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, defaultValueHelper.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,
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
//TODO: Custom Endpoint rol, permission seed
|
||
/*
|
||
Token İsteği Örnek:
|
||
POST /connect/token HTTP/1.1
|
||
Host: localhost:44344
|
||
Content-Type: application/x-www-form-urlencoded
|
||
|
||
username=system%40sozsoft.com
|
||
&password=...
|
||
&grant_type=password
|
||
&client_id=Platform_PublicApi
|
||
&scope=offline_access%20Platform
|
||
|
||
Custom Endpoint Seed:
|
||
SELECT * FROM PLanguage WHERE IsEnabled = @IsEnabled AND CultureName = @CultureName
|
||
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
|
||
string Url -> https://api.sozsoft.com/api/app/dinamik/yxfgu
|
||
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 = [
|
||
{ ResourceType: 'User', ResourceId: 'system' },
|
||
{ ResourceType: 'User', ResourceId: 'sedat' },
|
||
]
|
||
|
||
Query Parameter
|
||
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu
|
||
Method: GET
|
||
Parameters: ?StartDate=2024-12-31&EndDate=2025-12-31
|
||
|
||
Path Parameter
|
||
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu/fatura/467/kalem/357
|
||
Method: GET
|
||
Parameters: FaturaId=467
|
||
|
||
Body Parameter
|
||
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu?UrunId=5
|
||
Method: POST
|
||
Parameters: ?StartDate=2024-12-31&EndDate=2025-12-31
|
||
Body: { Tutar: 2000, Tarih: '2024-12-31' }
|
||
|
||
*/
|