Skip to content

Add support for stating accepts/consumes metadata is required or not #35849

Closed
@DamianEdwards

Description

@DamianEdwards

Follow on from #35421

Right now, the implicit request body parameter for an endpoint routing delegate (e.g. the todo in (Todo? todo) => { }) will flow whether it's required or not (based on the nullability declaration) to ApiExplorer (and thus OpenAPI libraries like Swashbuckle/nSwag).

However when manually describing the request body parameter via the .Accepts<TRequest>() extension methods or the Consumes attribute, there is no way to describe optionality. For the methods, using Accepts<TRequest?>() compiles but doesn't flow the nullability declaration as it isn't preserved by the compiler in a way that the method implementation can see that the generic type arg was declared as nullable. For the attribute (and the related metadata interface) there is no member to set that indicates whether the request body parameter is required or not. Right now when ApiExplorer is populated via this metadata, it's always assumed the body is required.

We should add the ability to describe whether the request body is required or not via the accepts/consumes metadata.

In addition, we should constrain the TRequest generic type argument on Accept<TRequest> to be non-nullable, so that users don't attempt to use the nullable reference type syntax there to describe optionality. We can work with the compiler team in an upcoming release to enable that if possible and in that case we can remove the constraint which is a non-breaking change.

Proposed API changes

namespace Microsoft.AspNetCore.Http
{
    public static class OpenApiEndpointConventionBuilderExtensions
    {
-        public static DelegateEndpointConventionBuilder Accepts<TRequest>(this DelegateEndpointConventionBuilder builder,  string contentType, params string[] additionalContentTypes);

+        public static DelegateEndpointConventionBuilder Accepts<TRequest>(this DelegateEndpointConventionBuilder builder,  string contentType, params string[] additionalContentTypes)
+          where TRequest : notnull;
+        public static DelegateEndpointConventionBuilder Accepts<TRequest>(this DelegateEndpointConventionBuilder builder,  bool isRequired, string contentType, params string[] additionalContentTypes)
+          where TRequest : notnull;

         public static DelegateEndpointConventionBuilder Accepts(this DelegateEndpointConventionBuilder builder, Type requestType, string contentType, params string[] additionalContentTypes);
+        public static DelegateEndpointConventionBuilder Accepts(this DelegateEndpointConventionBuilder builder, Type requestType, bool isRequired, string contentType, params string[] additionalContentTypes);
  }
}

namespace Microsoft.AspNetCore.Mvc
{
    public class ConsumesAttribute : Attribute, ...
    {
        public ConsumesAttribute(string contentType, params string[] otherContentTypes);
        public ConsumesAttribute(Type requestType, string contentType, params string[] otherContentTypes);
    
+       public bool IsRequired { get; set; } = true;
    }
}

namespace Microsoft.AspNetCore.Http.Metadata
{
    public interface IAcceptsMetadata
    {
       IReadOnlyList<string> ContentTypes { get; }
       Type? RequestType { get; }
+      bool IsRequired { get; }
    }
}

Example usage

app.MapPost("/todo", async (HttpRequest request) =>
    {
        var todo =  await request.Body.ReadAsJsonAsync<Todo>();
        return todo is Todo ? Results.Ok(todo) : Results.Ok();
    })
    .Accepts<Todo>(isRequired: false, contentType: "application/json");

app.MapPost("/todo", HandlePostTodo);
[Consumes(typeof(Todo), "application/json", IsRequired = false)]
IResult HandlePostTodo(HttpRequest request)
{
    var todo =  await request.Body.ReadAsJsonAsync<Todo>();
    return todo is Todo ? Results.Ok(todo) : Results.Ok();
}

Metadata

Metadata

Labels

Priority:1Work that is critical for the release, but we could probably ship withoutapi-approvedAPI was approved in API review, it can be implementedarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-minimal-actionsController-like actions for endpoint routingold-area-web-frameworks-do-not-use*DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions