Примите параметр перечисления из заголовка HTTP в действии контроллера в ASP.NET Core.C#

Место общения программистов C#
Ответить
Anonymous
 Примите параметр перечисления из заголовка HTTP в действии контроллера в ASP.NET Core.

Сообщение Anonymous »

В моем приложении ASP.NET Core я хочу предоставить конечную точку API, которая принимает один входной параметр следующего типа enum:

Код: Выделить всё

public enum MyEnum
{
[EnumMember(Name = "VALUE-1")]
VALUE_1,

[EnumMember(Name = "VALUE-2")]
VALUE_2
}
Я хочу, чтобы параметр был указан в HTTP-заголовке с именем X-CustomHeader, и я хочу использовать атрибуты [EnumMember] моих значений перечисления для десериализации (из-за ограничений на наименование свойств C#), поэтому я написал эту специальную привязку модели:

Код: Выделить всё

public class StringEnumMemberModelBinder : IModelBinder
{
Task IModelBinder.BindModelAsync(ModelBindingContext bindingContext)
{
ValueProviderResult valueProviderResult =
bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
string? rawData = valueProviderResult.FirstValue;

if (string.IsNullOrEmpty(rawData))
{
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
"The value is required."
);
bindingContext.Result = ModelBindingResult.Failed();
}
else if (EnumHelper.TryParse(rawData, bindingContext.ModelType, out Enum result)) // EnumHelper logic omitted
{
bindingContext.Result = ModelBindingResult.Success(result);
}
else
{
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
$"Could not parse value '{rawData}' as '{bindingContext.ModelType.FullName}'."
);
bindingContext.Result = ModelBindingResult.Failed();
}
}
}
Затем я применил связыватель модели на уровне конечной точки, используя атрибуты:

Код: Выделить всё

public class MyController
{
[HttpGet]
[Route("")]
public async Task Get(
[FromHeader(Name = "X-CustomHeader")]
[ModelBinder(typeof(StringEnumMemberModelBinder))]
MyEnum e
)
{
// [...]
}
}
Этот код завершается с ошибкой, поскольку поставщик значений связывателя модели не может получить значение заголовка (т. е. значение valueProviderResult.FirstValue имеет значение null в классе StringEnumMemberModelBinder). Тот же код работает при использовании атрибута [FromQuery], а не [FromHeader] и передаче моего параметра в строке запроса.
Я могу получить доступ к значению заголовка, используя контекст HTTP в объекте контекста привязки, но я бы предпочел, чтобы моя связка модели работала одинаково во всех случаях (когда источником параметра является строка запроса или заголовок HTTP).
Во время отладки я мог наблюдать, что bindingContext.ValueProvider имеет тип CompositeValueProvider. По сути, это набор поставщиков значений, доступных для операции. Когда я использую атрибут [FromHeader], коллекция состоит из RouteValueProvider и, возможно, QueryStringValueProvider, если в запросе присутствует строка запроса.
Похоже, что оба поставщика не могут обнаружить параметр в заголовке. С другой стороны, когда я использую [FromQuery], коллекция состоит только из QueryStringValueProvider (если строка запроса присутствует) и успешно обнаруживает параметр в строке запроса (как и ожидалось).
Я не уверен, что здесь происходит на самом деле. Я ожидал, что поведение будет аналогичным при использовании [FromQuery] или [FromHeader], т. е. поставщиком значения будет QueryStringValueProvider в первом случае и какой-то HeaderValueProvider во втором. Я вполне уверен, что это разумное ожидание, поскольку BindingSource контекста привязки правильно определяет предполагаемый источник параметра в обоих случаях ( в первом случае, Header во втором).
Как обойти все это?

Подробнее здесь: https://stackoverflow.com/questions/797 ... p-net-core
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»