ASP.NET Core updates available in .NET 7 Preview 3


ASP.NET Core improvements include support for Minimal API route handler filters, increased testability of Minimal API route handlers, binding in MVC controllers and API controllers with TryParse, and more. We share the material from the developers’ blog before the start C# development course.


To learn more about working on ASP.NET Core for .NET 7, see plan ASP.NET Core Development for .NET 7 on GitHub.

Let’s start

You can download the .NET 7 SDK here.

If you are on Windows and working with Visual Studio, we recommend installing the latest preview version Visual Studio 2022. Support for .NET 7 Preview 3 is not yet available on Visual Studio for Mac, but it will be soon.

To install the latest .NET WebAssembly assembly tools, run this elevated command:

dotnet workload install wasm-tools

Building .NET 6 Blazor projects with the .NET 7 SDK and .NET 7 WebAssembly assembly tools is not yet supported. We will fix it in update .NET 7.

Update an existing project

To upgrade an existing ASP.NET Core app from .NET 7 Preview 2 to .NET 7 Preview 3, change:

  • all references to Microsoft.AspNetCore.* packages are up to version 7.0.0-preview.3.*;

  • all references to Microsoft.Extensions.* packages are up to version 7.0.0-preview.3.*;

List of changes breaking backward compatibility see link.

Support for route handler filters in the Minimal API

Minimal application route handlers now support filters, they are executed before the execution of the main route handler logic. These filters can be used to inspect and modify the handler, as well as intercept its execution. These filters are registered in different ways. For example, through the RouteHandlerFilterDelegate and the AddFilter extension method:

app.MapGet("/hello/{name}", (string name) => $"Hello, {name}!")
    .AddFilter((context, next) =>
    {
        var name = (string) context.Parameters[0];
        if (name == "Bob")
        {
            return Result.Problem("No Bob's allowed");
        }
        return next(context);
    });

Or with a filter factory strategy that exposes the RouteHandlerContext. The latter, in turn, provides access to the MethodInfo associated with the handler, as well as to the metadata registered with the endpoint:

app.MapGet("/hello/{name}", (string name) => $"Hello, {name}!")
    .AddFilter((routeHandlerContext, next) =>
    {
        var parameters = routeHandlerContext.GetParameters();
        var hasCorrectSignature = parameters.Length == 1 && parameters[0].GetType() == typeof(string);
        return (context) =>
        {
            if (hasCorrectSignature)
            {
                var name = (string) context.Parameters[0];
                if (name == "Bob")
                {
                    return Result.Problem("No Bob's allowed");
                }
            }
            return next(context);
        }
    });

Filters can implement the IRouteHandlerFilter interface, resolve from dependency injection, or be passed as an instance:

app.MapGet("/hello/{name}", (string name) => $"Hello, {name}!")
    .AddFilter<MyFilter>();

Improved testability of Minimal API route handlers

IResult implementation types with suffix HttpResult (OkObjectHttpResult, ProblemHttpResult and so on) are now open in the namespace Microsoft.AspNetCore.Http. These types make it easier to test Minimal API route handlers instead of lambda expressions using named methods:

[Fact]
public async Task GetTodoReturnsTodoFromDatabase()
{
    var todo = new Todo { Id = 42, Name = "Improve Results testability!" };
    var mockDb = new MockTodoDb(new[] { todo });

    var result = (OkObjectHttpResult)await TodoEndpoints.GetTodo(mockDb, todo.Id);

    //Assert
    Assert.Equal(200, result.StatusCode);

    var foundTodo = Assert.IsAssignableFrom<Models.Todo>(result.Value);
    Assert.Equal(id, foundTodo.Id);
}

[Fact]
public void CreateTodoWithValidationProblems()
{
    //Arrange
    var newTodo = default(Todo);
    var mockDb = new MockTodoDb();

    //Act
    var result = TodoEndpoints.CreateTodo(mockDb, newTodo);

    //Assert        
    var problemResult = Assert.IsAssignableFrom<ProblemHttpResult>(result);
    Assert.NotNull(problemResult.ProblemDetails);
    Assert.Equal(400, problemResult.StatusCode);
}

Binding with TryParse in MVC Controllers and API Controllers

Controller action parameter values ​​can now be bound using the TryParse method with one of these signatures:

public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);

The following Get action binds data from the query string through the TryParse type parameter method:

public class TryParseController : ControllerBase
{
    // GET /tryparse?data=MyName
    [HttpGet]
    public ActionResult Get([FromQuery]CustomTryParseObject data) => Ok();

    public class CustomTryParseObject
    {
        public string? Name { get; set; }

        public static bool TryParse(string s, out CustomTryParseObject result)
        {
            if (s is null) 
            {
                result = default;
                return false;
            }

            result = new CustomTryParseObject { Name = s };
            return true;
        }
    }
}

New overloads of Results.Stream()

For situations where the underlying HTTP response stream needs to be accessed without buffering, new overloads of Results.Stream(…) have been written. These overloads make it easy to pass data to an HTTP response stream through an API, such as from Azure Blob storage. Below Results.Stream() is used in image processing via service ImageSharp:

app.MapGet("/process-image", async (HttpContext http) =>
{
    using var image = await Image.LoadAsync("puppy.jpeg");
    int width = image.Width / 2;
    int height = image.Height / 2;
    image.Mutate(x => x.Resize(width, height));
    http.Response.Headers.CacheControl = $"public,max-age={FromHours(24).TotalSeconds}";
    return Results.Stream(stream => image.SaveAsync(stream, PngFormat.Instance), "image/png");
});

Improved HTTP/2 connection performance with multiple streams

We have changed the HTTP/2 frame recording code. This code improves performance when there are multiple streams trying to write data on the same HTTP/2 connection. Now the TLS work is sent to the thread pool.

The write lock that other streams can acquire to write their data is released faster. When there is contention for that lock, reducing the wait time can greatly improve performance.

A gRPC test with 70 threads on a single connection with TLS showed an increase in requests per second of about 15%.

New Application Start Time Measurement Event – ServerReady

If you use an EventSource for measurements or diagnostics and want to measure the startup time of your application, you can use the new ServerReady event in the source Microsoft.AspNetCore.Hosting. This event represents the moment the server is up and running.

Dark theme for developer exceptions page

The ASP.NET Core developer exclusions page now supports dark theme:

Thank you for this improvement. @poke!

Feedback

We hope you enjoy this preview release of ASP.NET Core in .NET 7. Let us know what you think of these improvements by submitting Issues to GitHub. Thank you for trying ASP.NET Core!

And we will help you upgrade your skills or master an IT profession from the very beginning, which is relevant at any time:

Choose another in-demand profession.

Brief catalog of courses and professions

Data Science and Machine Learning

Python, web development

Mobile development

Java and C#

From basics to depth

As well as

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *