The best new features in ASP.NET Core 7

The latest version of Microsoft’s web application development framework brings excellent new capabilities to middleware, minimal API apps, and more. Here are the highlights.

shutterstock 2168992649
Patrick Krabeepetcharat / Shutterstock

A major part of Microsoft’s “cloud-first” .NET 7 release in November, ASP.NET Core 7 brings powerful new tools to the web development framework to help developers create modern web applications. Built on top of the .NET Core runtime, ASP.NET Core 7 has everything you need to build cutting-edge web user interfaces and robust back-end services on Windows, Linux, and macOS alike.

This article discusses the biggest highlights in ASP.NET Core 7, and includes some code examples to get you started with the new features. To work with the code examples provided in this article, you should have Visual Studio 2022 installed in your computer. If you don’t have a copy, you can download Visual Studio 2022 here.

Now let’s dive into the best new features in ASP.NET Core 7.

Output caching middleware

ASP.NET Core 7 allows you to use output caching in all ASP.NET Core apps: Minimal API, MVC, Razor Pages, and Web API apps with controllers. To add the output caching middleware to the services collection, invoke the IServiceCollection.AddOutputCache extension method. To add the middleware to the request processing pipeline, call the IApplicationBuilder.UseOutputCache extension method.

Then, to add a caching layer to an endpoint, you can use the following code.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!").CacheOutput();
app.Run();

Rate-limiting middleware

Controlling the rate at which clients can make requests to endpoints is an important security measure that allows web applications to ward off malicious attacks. You can prevent denial-of-service attacks, for example, by limiting the number of requests coming from a single IP address in a given period of time. The term for this capability is rate limiting.

The Microsoft.AspNetCore.RateLimiting middleware in ASP.NET Core 7 can help you enforce rate limiting in your application. You can configure rate-limiting policies and then attach those policies to the endpoints, thereby protecting those endpoints from denial-of-service attacks. This middleware is particularly useful for public-facing web applications that are susceptible to such attacks.

You can use the rate-limiting middleware with ASP.NET Core Web API, ASP.NET Core MVC, and ASP.NET Core Minimal API apps. To get started using this built-in middleware, add the Microsoft.AspNetCore.RateLimiting NuGet package to your project by executing the following command at the Visual Studio command prompt.

dotnet add package Microsoft.AspNetCore.RateLimiting

Alternatively, you can add this package to your project by running the following command in the NuGet package manager console or console window:

Install-Package Microsoft.AspNetCore.RateLimiting

Once the rate-limiting middleware has been installed, include the code snippet given below to your Program.cs file to add rate limiting services with the default configuration.

builder.Services.AddRateLimiter(options =>
{
});

Request decompression middleware

ASP.NET Core 7 includes a new request decompression middleware that allows endpoints to accept requests that have compressed content. This eliminates the need to write code explicitly to decompress requests with compressed content. It works by using a Content-Encoding HTTP header to identify and decompress compressed content in HTTP requests.

In response to an HTTP request that matches the Content-Encoding header value, the middleware encapsulates the HttpRequest.Body in a suitable decompression stream using the matching provider. This is followed by the removal of the Content-Encoding header, which indicates that the request body is no longer compressed. Note that the request decompression middleware ignores requests without a Content-Encoding header.

The code snippet below shows how you can enable request decompression for the default Content-Encoding types.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRequestDecompression();
var app = builder.Build();
app.UseRequestDecompression();
app.MapPost("/", (HttpRequest httpRequest) => Results.Stream(httpRequest.Body));
app.Run();

The default decompression providers are the Brotli, Deflate, and Gzip compression formats. Their Content-Encoding header values are br, deflate, and gzip, respectively. 

Filters in minimal APIs

Filters let you execute code during certain stages in the request processing pipeline. A filter runs before or after the execution of an action method. You can take advantage of filters to track web page visits or validate the request parameters. By using filters, you can focus on the business logic of your application rather than on writing code for the cross-cutting concerns in your application.

An endpoint filter enables you to intercept, modify, short-circuit, and aggregate cross-cutting issues such as authorization, validation, and exception handling. The new IEndpointFilter interface in ASP.NET Core 7 lets us design filters and connect them to API endpoints. These filters may change request or response objects or short-circuit request processing. An endpoint filter can be invoked on actions and on route endpoints.

The IEndpointFilter interface is defined in the Microsoft.AspNetCore.Http namespace as shown below.

public interface IEndpointFilter
{
    ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next);
}

The following code snippet illustrates how multiple endpoint filters can be chained together.

app.MapGet("/", () =>
{
    return "Demonstrating multiple endpoint filters.";
})
.AddEndpointFilter(async (endpointFilterInvocationContext, next) =>
    {
        app.Logger.LogInformation("This is the 1st filter.");
        var result = await next(endpointFilterInvocationContext);
        return result;
    })
.AddEndpointFilter(async (endpointFilterInvocationContext, next) =>
    {
        app.Logger.LogInformation("This is the 2nd filter.");
        var result = await next(endpointFilterInvocationContext);
        return result;
    })
.AddEndpointFilter(async (endpointFilterInvocationContext, next) =>
    {
        app.Logger.LogInformation("This is the 3rd Filter.");
        var result = await next(endpointFilterInvocationContext);
        return result;
    });

Parameter binding in action methods using DI

With ASP.NET Core 7, you can take advantage of dependency injection to bind parameters in the action methods of your API controllers. So, if the type is configured as a service, you no longer need to add the [FromServices] attribute to your method parameters. The following code snippet illustrates this.

[Route("[controller]")]
[ApiController]
public class MyDemoController : ControllerBase
{
    public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now);
}

Typed results in minimal APIs

The IResult interface was added in .NET 6 to represent values returned from minimal APIs that do not make use of the implicit support for JSON serializing the returned object. It should be noted here that the static Result class is used to create various IResult objects that represent different types of responses, such as setting a return status code or rerouting the user to a new URL. However, because the framework types returned from these methods were private, it wasn’t possible to verify the correct IResult type being returned from action methods during unit testing.

With .NET 7, the framework types that implement the IResult interface are now public. Thus we can use type assertions when writing our unit tests, as shown in the code snippet given below.

[TestClass()]
public class MyDemoApiTests
{
    [TestMethod()]
    public void MapMyDemoApiTest()
    {
        var result = _MyDemoApi.GetAllData();
        Assert.IsInstanceOfType(result, typeof(Ok<MyDemoModel[]>));
    }     
}

You can also use IResult implementation types to unit test your route handlers in minimal APIs. The following code snippet illustrates this.

var result = (Ok<MyModel>)await _MyDemoApi.GetAllData()

Route groups in minimal APIs

With ASP.NET Core 7, you can leverage the new MapGroup extension method to organize groups of endpoints that share a common prefix in your minimal APIs. The MapGroup extension method not only reduces repetitive code, but also makes it easier to customize entire groups of endpoints.

The following code snippet shows how MapGroup can be used.

app.MapGroup("/public/authors")
    .MapAuthorsApi()
    .WithTags("Public");

The next code snippet illustrates the MapAuthorsApi extension method.

public static class MyRouteBuilder
{ 
public static RouteGroupBuilder MapAuthorsApi(this RouteGroupBuilder group)
    {
        group.MapGet("/", GetAllAuthors);
        group.MapGet("/{id}", GetAuthor);
        group.MapPost("/", CreateAuthor);
        group.MapPut("/{id}", UpdateAuthor);
        group.MapDelete("/{id}", DeleteAuthor);
        return group;
    }
}

Health checks for gRPC

ASP.NET Core supports the use of the .NET Health Checks middleware to report the health of your application infrastructure components. ASP.NET Core 7 adds built-in support for monitoring the health of gRPC services by way of the Grpc.AspNetCore.HealthChecks NuGet package. You can use this package to expose an endpoint in your gRPC app that enables health checks.

Note that you would typically use health checks with an external monitoring system, or with a load balancer or container orchestrator. The latter might automate an action such as restarting or rerouting around the service based on health status. You can read more about ASP.NET Core health checks here.

File uploads in minimal APIs

You can now use IFormFile and IFormFileCollection in minimal APIs to upload files in ASP.NET Core 7. The following code snippet illustrates how IFormFile can be used.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/uploadfile", async (IFormFile iformFile) =>
{
    var tempFileName = Path.GetTempFileName();
    using var fileStream = File.OpenWrite(tempFileName);
    await iformFile.CopyToAsync(fileStream);
});
app.Run();

If you want to upload multiple files, you can use the following piece of code instead.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/uploadfiles", async (IFormFileCollection files) =>
{
    foreach (var file in files)
    {
        var tempFileName = Path.GetTempFileName();
        using var fileStream = File.OpenWrite(tempFileName);
        await file.CopyToAsync(fileStream);
    }
});
app.Run();

From output caching, rate-limiting, and request decompression middleware to filters, route maps, and other new capabilities for minimal APIs, ASP.NET Core 7 gives developers many new features that will make it much easier and faster to create robust web applications. ASP.NET Core 7 enables faster development using modular components, offers better performance and scalability across multiple platforms, and simplifies deployment to web hosts, Docker, Azure, and other hosting environments with built-in tooling.

In this article we’ve discussed only some of the important new features in ASP.NET Core 7—my top picks. You can find the complete list of new features in ASP.NET Core 7 here.

Copyright © 2023 IDG Communications, Inc.