День 1416. #ЧтоНовенького
Новые Возможности MVC в .NET 7. Часть 3
В этой серии рассмотрим некоторые нововведения в ASP.NET Core в .NET 7.
Часть 1
Часть 2
3. Использование nullable-аннотаций
С каждым выпуском .NET поддержка ссылочных обнуляемых типов становится немного лучше. Для ASP.NET Core в .NET 7 она была добавлена в нескольких местах:
1) Обнуляемое тело запроса
В .NET 6 при привязке к телу запроса (независимо от того, используется ли явно [FromBody]), попытка привязки к запросу с пустым телом (где Content-Length == 0) завершится ошибкой "A non-empty request body is required." (Требуется непустое тело запроса).
Вы можете обойти это глобально в .NET 6 и разрешить null для параметра, настроив свойство MvcOptions.AllowEmptyInputInBodyModelBinding:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<MvcOptions>(opts =>
{
opts.AllowEmptyInputInBodyModelBinding = true;
});
Другой вариант - включить его отдельно для каждого метода действия, добавив явный атрибут [FromBody] и установив EmptyBodyBehavior, например:
public IActionResult Post(
[FromBody(EmptyBodyBehavior=EmptyBodyBehavior.Allow)]
MyBody? body)
{
// body будет null при пустом теле
}
Это работает, но это слишком длинно и уродливо…
В .NET 7 всё становится намного проще. Вы можете полагаться на nullable-аннотации. Обнуляемый параметр подразумевает EmptyBodyBehavior.Allow, а необнуляемый - EmptyBodyBehavior.Disallow:
public IActionResult Post(MyBody? body)
{
// body будет null при пустом теле
}
public IActionResult Post(MyBody body)
{
// При пустом теле будет ошибка 400
}
2) Определение необязательности сервиса
В .NET 6, если вы не зарегистрировали тип сервиса в контейнере внедрения зависимостей, то, даже если декорированный атрибутом [FromService] параметр помечен как обнуляемый, попытка вызова конечной точки API приводит к ошибке:
InvalidOperationException: No service for type 'SomeService' has been registered (Сервис типа 'SomeService' не был зарегистрирован)
В .NET 7 обнуляемый параметр с атрибутом [FromServices] будет null, если он недоступен в DI:
public IEnumerable<MyClass> Get(
[FromServices] SomeService? service)
{ … }
Вам может быть интересно, что произойдёт, если вы попытаетесь привязать обнуляемый сервис, который не зарегистрирован в DI, используя автоматическое определение сервиса, описанное в части 2?
Ответ: скорее всего, не то, чего вы хотите. Если тип параметра (SomeService выше) не зарегистрирован в DI, он не будет рассматриваться как сервис, он будет рассматриваться как любой другой сложный тип. Это означает, что MVC попытается привязать его свойства к любым доступным параметрам: телу запроса, строке запроса, заголовкам и т.п.
Источник: https://andrewlock.net/5-new-mvc-features-in-dotnet-7/