Back to Aspnetcore

OpenAPI Completion

aspnetcore/release-notes/aspnetcore-9/includes/openAPI_completion.md

latest4.9 KB
Original Source

Intellisense completion enhancements for OpenAPI package

ASP.NET Core's OpenAPI support is now more accessible and user-friendly. The OpenAPI APIs are shipped as an independent package, separate from the shared framework. Until now, this meant that developers didn't have the convenience of code-completion features like Intellisense for OpenAPI APIs.

Recognizing this gap, we have introduced a new completion provider and code fixer. These tools are designed to facilitate the discovery and use of OpenAPI APIs, making it easier for developers to integrate OpenAPI into their projects. The completion provider offers real-time code suggestions, while the code fixer assists in correcting common mistakes and improving API usage. This enhancement is part of our ongoing commitment to improve the developer experience and streamline API-related workflows.

When a user types a statement where an OpenAPI-related API is available, the completion provider displays a recommendation for the API. For example, in the following screenshots, completions for xref:Microsoft.Extensions.DependencyInjection.OpenApiServiceCollectionExtensions.AddOpenApi and xref:Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions.MapOpenApi are provided when a user is entering an invocation statement on a supported type, such as xref:Microsoft.AspNetCore.Builder.IEndpointConventionBuilder:

When the completion is accepted and the Microsoft.AspNetCore.OpenApi package is not installed, a codefixer provides a shortcut for automatically installing the dependency in the project.

Support for [Required] and [DefaultValue] attributes on parameters and properties

When [Required] and [DefaultValue] attributes are applied on parameters or properties within complex types, the OpenAPI implementation maps these to the required and default properties in the OpenAPI document associated with the parameter or type schema.

For example, the following API produces the accompanying schema for the Todo type.

csharp
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.MapPost("/todos", (Todo todo) => { });

app.Run();

class Todo
{
	public int Id { get; init; }
	public required string Title { get; init; }
	[DefaultValue("A new todo")]
	public required string Description { get; init; }
	[Required]
	public DateTime CreatedOn { get; init; }
}
json
{
	"required": [
	  "title",
	  "description",
	  "createdOn"
	],
	"type": "object",
	"properties": {
	  "id": {
	    "type": "integer",
	    "format": "int32"
	  },
	  "title": {
	    "type": "string"
	  },
	  "description": {
	    "type": "string",
	    "default": "A new todo"
	  },
	  "createdOn": {
	    "type": "string",
	    "format": "date-time"
	  }
	}
}

Support for schema transformers on OpenAPI documents

Built-in OpenAPI support now ships with support for schema transformers that can be used to modify schemas generated by xref:System.Text.Json?displayProperty=fullName and the OpenAPI implementation. Like document and operation transformers, schema transformers can be registered on the xref:Microsoft.AspNetCore.OpenApi.OpenApiOptions object. For example, the following code sample demonstrates using a schema transformer to add an example to a type's schema.

csharp
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Microsoft.OpenApi.Any;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddSchemaTransformer((schema, context, cancellationToken) =>
    {
        if (context.JsonTypeInfo.Type == typeof(Todo))
        {
            schema.Example = new OpenApiObject
            {
                ["id"] = new OpenApiInteger(1),
                ["title"] = new OpenApiString("A short title"),
                ["description"] = new OpenApiString("A long description"),
                ["createdOn"] = new OpenApiDateTime(DateTime.Now)
            };
        }
        return Task.CompletedTask;
    });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.MapPost("/todos", (Todo todo) => { });

app.Run();

class Todo
{
	public int Id { get; init; }
	public required string Title { get; init; }
	[DefaultValue("A new todo")]
	public required string Description { get; init; }
	[Required]
	public DateTime CreatedOn { get; init; }
}