РЕДАКТИРОВАТЬ 2: Иногда вы думаете, что так сложно, что вы упускаете очевидное.Решение для динамической маршрутизации с OData:
Startup.cs
app.UseMvc(b =>
{
b.Select().Filter().OrderBy().MaxTop(100).Count();
b.MapODataServiceRoute(
routeName: "odata",
routePrefix: "api/tenants/{tenantId}/odata",
model: ODataHelper.GetEdmModel());
});
Контроллер:
[HttpGet]
[EnableQuery]
[ODataRoute("Clients")]
public async Task<IEnumerable<Client>> Get(
ODataQueryOptions<Client> options,
[FromRoute] Guid tenantId)
{
IQueryable res = await _service.Get(
tenantId,
AuthorizationHelper.GetSubjectId(tenantId, User),
AuthorizationHelper.GetAllowedUserRoles(RoleType.Reporting),
options,
null);
return new List<Client>(res.Cast<Client>());
}
Я оставляю здесь свой обходной путь дляВ случае, если кто-то может его использовать:
После значительного исследования в Реализация OData .Net Core Я наконец-то обнаружил, что моя первая предоставленная ссылка " ODataMediaTypeFormatter в WebApi"уже предоставил решение моего обходного пути.
Во-первых, BaseAddressFactory
может выполнять только данный HTTP-запрос.Поэтому мне нужно было изменить следующий код:
public static (Microsoft.AspNetCore.Http.HttpContext, Guid) ParseTenantIDToContext(Microsoft.AspNetCore.Http.HttpContext context)
{
System.Guid tenantGuid = System.Guid.Empty;
if (context.Request.Path.ToString().Split('/').Length > 3 && context.Request.Path.ToString().ToLower().Contains("odata"))
{
bool isValidGUID = System.Guid.TryParse(context.Request.Path.ToString().Split('/')[3], result: out tenantGuid);
if (isValidGUID)
{
context.Request.Path = context.Request.Path.Value.Replace(context.Request.Path.ToString().Split('/')[3], "tenantId");
context.Items["tenantId"] = tenantGuid.ToString();
context.Request.Headers.Remove("tenantId");
context.Request.Headers.Append("tenantId", tenantGuid.ToString());
}
}
return (context, tenantGuid);
}
В этом разделе я сохраняю требуемый tenantId
не только в HTTPContext, но и как специальный заголовок в HTTPRequest.
* 1028Основное решение - предоставить специальную функцию
BaseAddressFactory
, которая управляет базовым адресом, который OData использует для создания метаданных.В качестве реализации я добавляю следующий код в
ConfigureServices
после добавления OData через
services.AddOData()
:
services.AddMvc(op =>
{
foreach (var formatter in op.OutputFormatters
.OfType<ODataOutputFormatter>())
{
formatter.BaseAddressFactory = ODataHelper.CustomBaseAddressFactory;
}
foreach (var formatter in op.InputFormatters
.OfType<ODataInputFormatter>())
{
formatter.BaseAddressFactory = ODataHelper.CustomBaseAddressFactory;
}
});
My ODataHelper.CustomBaseAddressFactory
выглядит следующим образом:
public static Uri CustomBaseAddressFactory (HttpRequest request)
{
Guid tenantGuid = GetTenantIDFromRequest(request);
request.Headers.Remove("tenantId");
Uri std = ODataInputFormatter.GetDefaultBaseAddress(request);
string ret = replaceTentantIdInURL(std.ToString(), tenantGuid);
return ret[ret.Length - 1] != '/' ? new Uri(ret + '/') : new Uri(ret);
}
Чтобы предоставить стольконасколько это возможно, я использую стандарт ODataInputFormatter.GetDefaultBaseAddress
, а затем снова заменяю статический заполнитель.
РЕДАКТИРОВАТЬ
Этот способ сохранения tenantId
довольно небезопасен, посколькуЗаголовки запроса также могут быть созданы конечным пользователем.В конце концов, я решил получить ID из наших требований авторизации, которые его предоставляют.Поэтому пользователь не может атаковать этот обходной путь.