ASP.NET Core updates in .NET 7 Preview 1
.NET 7 Preview 1 is now available!. This is the first preview of the next major version of .NET, which will include the next wave of innovations for web development with ASP.NET Core.
In .NET 7 we plan to make broad investments across ASP.NET Core. Below are some of the areas we plan to focus on:
- Performance: .NET 6 contained many performance improvements for ASP.NET Core, and we’ll do work to make ASP.NET Core even faster and more efficient in .NET 7.
- HTTP/3: HTTP/3 support shipped as a preview feature in .NET 6. For .NET 7, we want to finish it and make it a supported feature that’s enabled by default. In future previews, you can expect to see advanced TLS features and more performance improvements in our HTTP/3 support.
- Minimal APIs: Add support for endpoint filters and route grouping as core primitives for minimal APIs. Also simplify authentication and authorization configurations for APIs in general.
- gRPC: We’re investing in gRPC JSON transcoding. This feature allows gRPC services to be called like RESTful HTTP APIs with JSON requests and responses.
- SignalR: Add support for strongly-typed clients and returning results from client invocations.
- Razor: We’ll make various improvements to the Razor compiler to improve performance, resiliency, and to facilitate improved tooling.
- Blazor: After finishing Blazor Hybrid support for .NET MAUI, WPF, and Windows Forms, we’ll make broad improvements to Blazor including:
- New .NET WebAssembly capabilities: mixed-mode AOT, multithreading, web crypto.
- Enhanced Hot Reload support.
- Data binding improvements.
- More flexible prerendering.
- More control over the lifecycle of Blazor Server circuits.
- Improved support for micro frontends.
- MVC: Improvements to endpoint routing, link generation, and parameter binding.
- Orleans: The ASP.NET Core and Orleans teams are investigating ways to further align and integrate the Orleans distributed programming model with ASP.NET Core. Orleans 4 will ship alongside .NET 7 and focuses on simplicity, maintainability, and performance, including human readable stream identities and a new optimized, version-tolerant serializer.
For more details on the specific ASP.NET Core work planned for .NET 7 see the full ASP.NET Core roadmap for .NET 7 on GitHub.
.NET 7 Preview 1 is the first of many .NET 7 preview releases in preparation for the .NET 7 release in November 2022.
I joined James Montemagno on a recent episode of On .NET to break down all of what is coming in .NET 7 and ASP.NET Core in .NET 7:
Here’s a summary of what’s new in this preview release:
- Minimal API improvements:
IFormFile
andIFormFileCollection
Support- Bind the request body as a
Stream
orPipeReader
- JSON options configuration
- SignalR client source generator
- Support for nullable models in MVC views and Razor Pages
- Use JSON property names in validation errors
- Improved console output for
dotnet watch
- Configure
dotnet watch
to always restart for rude edits - Use dependency injection in a
ValidationAttribute
- Faster header parsing and writing
- gRPC JSON transcoding
Get started
To get started with ASP.NET Core in .NET 7 Preview 1, install the .NET 7 SDK.
If you’re on Windows using Visual Studio, we recommend installing the latest Visual Studio 2022 preview. Visual Studio for Mac support for .NET 7 previews isn’t available yet but is coming soon.
To install the latest .NET WebAssembly build tools, run the following command from an elevated command prompt:
dotnet workload install wasm-tools
Upgrade an existing project
To upgrade an existing ASP.NET Core app from .NET 6 to .NET 7 Preview 1:
- Update the target framework for your app to
net7.0
. - Update all Microsoft.AspNetCore.* package references to
7.0.0-preview.1.*
. - Update all Microsoft.Extensions.* package references to
7.0.0-preview.1.*
.
See also the full list of breaking changes in ASP.NET Core for .NET 7.
Minimal API improvements
IFormFile
and IFormFileCollection
Support
You can now handle file uploads in minimal APIs using IFormFile
and IFormFileCollection
:
app.MapPost("/upload", async(IFormFile file) =>
{
using var stream = System.IO.File.OpenWrite("upload.txt");
await file.CopyToAsync(stream);
});
app.MapPost("/upload", async (IFormFileCollection myFiles) => { ... });
Using this feature with authentication requires anti-forgery support, which isn’t yet implemented. Anti-forgery support for minimal APIs is on our roadmap for .NET 7. Binding to IFormFile
or IFormFileCollection
when the request contains an Authorization
header, a client certificate, or a cookie header is currently disabled. This limitation will be addressed as soon as we complete the work on anti-forgery support.
Thanks to @martincostello for contributing this feature.
Bind the request body as a Stream
or PipeReader
You can now bind the request body as a Stream
or PipeReader
to efficiently support scenarios where the user has to ingest data and either store it to a blob storage or enqueue the data to a queue provider (Azure Queue, etc.) for later processing by a worker or cloud function. The following example shows how to use the new binding:
app.MapPost("v1/feeds", async (QueueClient queueClient, Stream body, CancellationToken cancellationToken) =>
{
await queueClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
await queueClient.SendMessageAsync(await BinaryData.FromStreamAsync(body), cancellationToken: cancellationToken);
});
When using the Stream
or PipeReader
there are a few things to take into consideration:
- When ingesting data, the
Stream
will be the same object asHttpRequest.Body
. - The request body isn’t buffered by default. After the body is read, it’s not rewindable (you can’t read the stream multiple times).
- The
Stream/PipeReader
are not usable outside of the minimal action handler as the underlying buffers will be disposed and/or reused.
JSON options configuration
We’re introducing a new and cleaner API, ConfigureRouteHandlerJsonOptions
, to configure JSON options for minimal API endpoints. This new API avoids confusion with Microsoft.AspNetCore.Mvc.JsonOptions
.
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureRouteHandlerJsonOptions(options =>
{
//Ignore Cycles
options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
SignalR client source generator
We’ve added a new client source generator for SignalR thanks to a contribution by @mehmetakbulut.
The SignalR client source generator generates strongly-typed sending and receiving code based on interfaces that you define. You can reuse the same interfaces from strongly-typed SignalR hubs on the client in place of the loosely-typed .On("methodName", ...)
methods. Similarly, your hub can implement an interface for its methods and the client can use that same interface to call the hub methods.
To use the SignalR client source generator:
- Add a reference to the Microsoft.AspNetCore.SignalR.Client.SourceGenerator package.
- Add a
HubServerProxyAttribute
andHubClientProxyAttribute
class to your project (this part of the design will likely change in future previews):
[AttributeUsage(AttributeTargets.Method)]
internal class HubServerProxyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal class HubClientProxyAttribute : Attribute
{
}
- Add a static partial class to your project and write static partial methods with the
[HubClientProxy]
and[HubServerProxy]
attributes
internal static partial class MyCustomExtensions
{
[HubClientProxy]
public static partial IDisposable ClientRegistration<T>(this HubConnection connection, T provider);
[HubServerProxy]
public static partial T ServerProxy<T>(this HubConnection connection);
}
- Use the partial methods from your code!
public interface IServerHub
{
Task SendMessage(string message);
Task<int> Echo(int i);
}
public interface IClient
{
Task ReceiveMessage(string message);
}
public class Client : IClient
{
// Equivalent to HubConnection.On("ReceiveMessage", (message) => {});
Task ReceiveMessage(string message)
{
return Task.CompletedTask;
}
}
HubConnection connection = new HubConnectionBuilder().WithUrl("...").Build();
var stronglyTypedConnection = connection.ServerProxy<IServerHub>();
var registrations = connection.ClientRegistration<IClient>(new Client());
await stronglyTypedConnection.SendMessage("Hello world");
var echo = await stronglyTypedConnection.Echo(10);
Support for nullable models in MVC views and Razor Pages
We enabled defining a nullable page or view model to improve the experience when using null state checking with ASP.NET Core apps:
@model Product?
Use JSON property names in validation errors
When model validation produces a ModelErrorDictionary
it will by default use the property name as the error key ("MyClass.PropertyName"
). Model property names are generally an implementation detail, which can make them difficult to handle from single-page apps. You can now configure validation to use the corresponding JSON property names instead with the new SystemTextJsonValidationMetadataProvider
(or NewtonsoftJsonValidationMetadataProvider
when using Json.NET).
services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider())
});
Improved console output for dotnet watch
We cleaned up the console output from dotnet watch
to better align with the log out of ASP.NET Core and to stand out with 😮emojis😍.
Here’s an example of what the new output looks like:
C:BlazorApp> dotnet watch
dotnet watch 🔥 Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload.
💡 Press "Ctrl + R" to restart.
dotnet watch 🔧 Building...
Determining projects to restore...
All projects are up-to-date for restore.
You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
BlazorApp -> C:UsersdarothDesktopBlazorAppbinDebugnet7.0BlazorApp.dll
dotnet watch 🚀 Started
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7148
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5041
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:UsersdarothDesktopBlazorApp
dotnet watch ⌚ File changed: .PagesIndex.razor.
dotnet watch 🔥 Hot reload of changes succeeded.
info: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
dotnet watch 🛑 Shutdown requested. Press Ctrl+C again to force exit.
Configure dotnet watch
to always restart for rude edits
Configure dotnet watch
to always restart without a prompt for rude edits (edits that can’t be hot reloaded) by setting the DOTNET_WATCH_RESTART_ON_RUDE_EDIT
environment variable to true
.
Inject services into custom validation attributes in Blazor
You can now inject services into custom validation attributes in Blazor. Blazor will setup the ValidationContext
so it can be used as a service provider.
public class SaladChefValidatorAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var saladChef = validationContext.GetRequiredService<SaladChef>();
if (saladChef.ThingsYouCanPutInASalad.Contains(value.ToString()))
{
return ValidationResult.Success;
}
return new ValidationResult("You should not put that in a salad!");
}
}
// Simple class configured as a service for dependency injection
public class SaladChef
{
public string[] ThingsYouCanPutInASalad = { "Strawberries", "Pineapple", "Honeydew", "Watermelon", "Grapes" };
}
Thank you @MariovanZeist for this contribution!
Faster header parsing and writing
We made several improvements to the performance of header parsing and writing for HTTP/2 and HTTP/3. See the following pull requests for details:
- HTTP/2: Improve incoming header performance
- HTTP/3: Optimize validating and setting incoming headers
- HTTP headers enumerator move directly to next
gRPC JSON transcoding
gRPC JSON transcoding allows gRPC services to be used like a RESTful HTTP APIs. Once configured, gRPC JSON transcoding allows you to call gRPC methods with familiar HTTP concepts:
- HTTP verbs
- URL parameter binding
- JSON requests/responses
Of course gRPC can continue to be used as well. RESTful APIs for your gRPC services. No duplication!
ASP.NET Core has experimental support for this feature using a library called gRPC HTTP API. For .NET 7 we plan to make this functionality a supported part of ASP.NET Core. This functionality isn’t included with .NET 7 yet, but you can try out the existing experimental packages. For more information, see the gRPC HTTP API getting started documentation.
Give feedback
We hope you enjoy this preview release of ASP.NET Core in .NET 7 and that you’re as excited about about our roadmap for .NET 7 as we are! We’re eager to hear about your experiences with this release and your thoughts on the roadmap. Let us know what you think by filing issues on GitHub and commenting on the roadmap issue.
Thanks for trying out ASP.NET Core!
32 comments
Hype over Blazor.
I’m using it in production and it’s great!
I want to shout about the need for comprehensive development, the main improvement of the BlazorHub algorithm. It’s scary that only a few people seem to be working on it.
Had a look at the gRPC HTTP API GitHub page and found this comment
Is that still the case?
The gRPC HTTP API project started out as an experimental project in the AspLabs repo, but we are planning to turn it into a supported feature for .NET 7. We’ll update the readme on the experimental project to make that clearer.
Everything looks great and I’m excited about it. But, support for emojis in console output? Feel like that’s crossing a technology line we can’t come back from. Have you considered the cultural and technical implications of your design decisions on this one?
Thanks for your comment. Regarding the emoji use in the
dotnet watch
output it’s something we’re trying out. This is why we do previews! We aren’t using directly culturally associated symbols like flags or people and the feature can be disabled with an environment variable. The tool itself is intended to be interactive with its output seen and responded to by an actual human so this seemed like a reasonable approach to implement and assess. We’ve tested it out on Windows, Mac, and Linux in different shells and welcome feedback about how it works for folks in their environment.Emojis are great for getting a lot of information with a quick glance. Been using them in CI pipelines for about a year now, and looking forward to seeing them in more places.
Keep it up)
Sorry, I was only trying to make a joke 🙂
Honestly, I think emojis in console output can be a useful feature. Thank you for the update!
Blazor doesn’t have full AOT yet as reported in Blazor AOT .Net6 Preview 7 – Still downloading Dlls #35302, yet this issue is closed and full AOT is marked as complete (Developers can AOT compile .NET apps into WebAssembly format), and now Microsoft is moving to Mixed AOT as if full AOT were already there. Full AOT is pretty essential for deploying apps on browsers with good startup performance and file size. I think better for devs who want to deploy apps on browsers to give up on Blazor and focus on the work done in runtimelab on NativeAOT WASM which seems a lot closer to having a usable wasm compilation toolchain.
Hi Charles.
You’re correct that we still rely on .NET IL interpretation in some cases when using ahead-of-time (AOT) compilation for WebAssembly. This is because certain .NET runtime features still require it. So “full” AOT in this case really means we AOT compile as much as we can. There is ongoing work to further improve WebAssembly AOT to reduce the need for using the interpreter, but it’s unlikely we’ll be able to remove it completely given the limitations WebAssembly currently imposes.
Please also note that if download size is an issue, AOT compilation in general isn’t going to help even if we could fully remove the need for interpretation. AOT compiled code is generally larger than the original .NET IL instructions, about 2x larger. As we further reduce the need for interpretation, we can somewhat reduce the size of the app by further trimming the .NET assemblies, but the app will still be larger than if it wasn’t using AOT at all. Where WebAssembly AOT compilation helps is with improving runtime performance for CPU intensive work.
We’re also aware of the NativeAOT LLVM backend work and excited to see how it plays out!
Thanks for the reply.
It may be that certain runtime features make some things with full AOT impossible but in that case they should be tracked so that .Net devs know what they are waiting for, or alternatively can adopt NativeAOT (perhaps avoiding those constructs if they affect all dotnet code and not just mono). It will also make it clearer to WASM devs what is needed for .Net support.
I don’t believe that AOT hurts download size significantly because the smallest download sizes in .Net come from NativeAOT. For example this https://medium.com/@MStrehovsky/building-a-self-contained-game-in-c-under-8-kilobytes-74c3cf60ea04 gets to 1.2MB using reasonable techniques (no reflection) and that is perfectly good for .Net on the web. (It gets to 8kb using techniques that are not possible for most dotnet apps.) Also if the size is “2x”, and that includes IL, then when you remove the IL it will be closet to 1x. As far as I can tell within .Net, evidence that AOT gives large sizes comes only from mono, and NativeAOT and .Net Native did not suffer.
I’m doing more and more of my work in Blazor, but one feature I’m missing is some kind of WebView for Linux. If there’s already enough API surface to make it work, perhaps those experts on the Blazor team could create a blog post illustrating how to do it. With Edge now on Linux, how can one employ it in a WebView on Linux?
We expect to bring Blazor Hybrid support to Linux via .NET MAUI once .NET MAUI has Linux support.
I can’t find the razor.g.cs files anymore, it was interesting to see how the components were compiled ….
Hi Lucio. You don’t see the razor.g.cs files anymore because we switched Razor to use source generators. I believe you can see the output if you set
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
(details)Hi Daniel. Perfect, it works. Many thanks.
Fantastic work with gRPC transcoding and strongly typed SignalR hubs!
Hi Daniel.
Thanks to you and your team for your work. In my company we use and appreciate Blazor from the begining. Multithreading will be game changer, can’t wait!
It would be nice to see on-demand ISR(Incremental Static Regeneration) for Blazor. On-demand ISR is the most requested feature of Next.Js
Hi,
Thank you for the update and all the great work!
Can you provide more information on what “Improved support for micro frontends” means?
On a related note, the current implementation of Blazor lazy loading is unusable in real-world projects. Please review the issue that I opened on GitHub:
https://github.com/dotnet/aspnetcore/issues/39622
I understand that there are limitations on what .NET offers but Blazor is already pushing the boundaries of the framework, so this may be another area to investigate.
Also, is lazy loading going to work in the mixed-mode AOT?
Thank you for your consideration!
Hi Daniel. Better support for micro frontends includes support for running multiple Blazor Server or Blazor WebAssembly apps on the same page and support for building standards based custom elements with Blazor.