{"id":38839,"date":"2022-02-17T09:46:10","date_gmt":"2022-02-17T16:46:10","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=38839"},"modified":"2022-03-16T12:56:16","modified_gmt":"2022-03-16T19:56:16","slug":"asp-net-core-updates-in-net-7-preview-1","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-updates-in-net-7-preview-1\/","title":{"rendered":"ASP.NET Core updates in .NET 7 Preview 1"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-7-preview-1\">.NET 7 Preview 1 is now available!<\/a>. 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.<\/p>\n<p>In .NET 7 we plan to make broad investments across ASP.NET Core. Below are some of the areas we plan to focus on:<\/p>\n<ul>\n<li><strong>Performance<\/strong>: .NET 6 contained many <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-aspnet-core-6\/\">performance improvements for ASP.NET Core<\/a>, and we&#8217;ll do work to make ASP.NET Core even faster and more efficient in .NET 7.<\/li>\n<li><strong>HTTP\/3<\/strong>: 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&#8217;s enabled by default. In future previews, you can expect to see advanced TLS features and more performance improvements in our HTTP\/3 support.<\/li>\n<li><strong>Minimal APIs<\/strong>: Add support for endpoint filters and route grouping as core primitives for minimal APIs. Also simplify authentication and authorization configurations for APIs in general.<\/li>\n<li><strong>gRPC<\/strong>: We&#8217;re investing in gRPC JSON transcoding. This feature allows gRPC services to be called like RESTful HTTP APIs with JSON requests and responses.<\/li>\n<li><strong>SignalR<\/strong>: Add support for strongly-typed clients and returning results from client invocations.<\/li>\n<li><strong>Razor<\/strong>: We&#8217;ll make various improvements to the Razor compiler to improve performance, resiliency, and to facilitate improved tooling.<\/li>\n<li><strong>Blazor<\/strong>: After finishing Blazor Hybrid support for .NET MAUI, WPF, and Windows Forms, we&#8217;ll make broad improvements to Blazor including:\n<ul>\n<li>New .NET WebAssembly capabilities: mixed-mode AOT, multithreading, web crypto.<\/li>\n<li>Enhanced Hot Reload support.<\/li>\n<li>Data binding improvements.<\/li>\n<li>More flexible prerendering.<\/li>\n<li>More control over the lifecycle of Blazor Server circuits.<\/li>\n<li>Improved support for micro frontends.<\/li>\n<\/ul>\n<\/li>\n<li><strong>MVC<\/strong>: Improvements to endpoint routing, link generation, and parameter binding.<\/li>\n<li><strong>Orleans<\/strong>: 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.<\/li>\n<\/ul>\n<p>For more details on the specific ASP.NET Core work planned for .NET 7 see the full <a href=\"https:\/\/aka.ms\/aspnet\/roadmap\">ASP.NET Core roadmap for .NET 7<\/a> on GitHub.<\/p>\n<p>.NET 7 Preview 1 is the first of many .NET 7 preview releases in preparation for the .NET 7 release in November 2022. <\/p>\n<p>I joined <a href=\"https:\/\/twitter.com\/jamesmontemagno\" target=\"_blank\" rel=\"noopener\">James Montemagno<\/a> 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:<\/p>\n<p><iframe width=\"560\" height=\"315\" src=\"https:\/\/www.youtube-nocookie.com\/embed\/VgmsFck3RWU\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><\/p>\n<p>Here&#8217;s a summary of what&#8217;s new in this preview release:<\/p>\n<ul>\n<li>Minimal API improvements:\n<ul>\n<li><code>IFormFile<\/code> and <code>IFormFileCollection<\/code> Support<\/li>\n<li>Bind the request body as a <code>Stream<\/code> or <code>PipeReader<\/code><\/li>\n<li>JSON options configuration<\/li>\n<\/ul>\n<\/li>\n<li>SignalR client source generator<\/li>\n<li>Support for nullable models in MVC views and Razor Pages<\/li>\n<li>Use JSON property names in validation errors<\/li>\n<li>Improved console output for <code>dotnet watch<\/code><\/li>\n<li>Configure <code>dotnet watch<\/code> to always restart for rude edits<\/li>\n<li>Use dependency injection in a <code>ValidationAttribute<\/code><\/li>\n<li>Faster header parsing and writing<\/li>\n<li>gRPC JSON transcoding<\/li>\n<\/ul>\n<h3>Get started<\/h3>\n<p>To get started with ASP.NET Core in .NET 7 Preview 1, <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/7.0\">install the .NET 7 SDK<\/a>.<\/p>\n<p>If you&#8217;re on Windows using Visual Studio, we recommend installing the latest <a href=\"https:\/\/visualstudio.com\/preview\">Visual Studio 2022 preview<\/a>. Visual Studio for Mac support for .NET 7 previews isn&#8217;t available yet but is coming soon.<\/p>\n<p>To install the latest .NET WebAssembly build tools, run the following command from an elevated command prompt:<\/p>\n<pre><code class=\"language-console\">dotnet workload install wasm-tools<\/code><\/pre>\n<h3>Upgrade an existing project<\/h3>\n<p>To upgrade an existing ASP.NET Core app from .NET 6 to .NET 7 Preview 1:<\/p>\n<ul>\n<li>Update the target framework for your app to <code>net7.0<\/code>.<\/li>\n<li>Update all Microsoft.AspNetCore.* package references to <code>7.0.0-preview.1.*<\/code>.<\/li>\n<li>Update all Microsoft.Extensions.* package references to <code>7.0.0-preview.1.*<\/code>.<\/li>\n<\/ul>\n<p>See also the full list of <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/7.0#aspnet-core\">breaking changes<\/a> in ASP.NET Core for .NET 7.<\/p>\n<h2>Minimal API improvements<\/h2>\n<h3><code>IFormFile<\/code> and <code>IFormFileCollection<\/code> Support<\/h3>\n<p>You can now handle file uploads in minimal APIs using <code>IFormFile<\/code> and <code>IFormFileCollection<\/code>: <\/p>\n<pre><code class=\"language-csharp\">app.MapPost(\"\/upload\", async(IFormFile file) =&gt;\r\n{\r\n    using var stream = System.IO.File.OpenWrite(\"upload.txt\");\r\n    await file.CopyToAsync(stream); \r\n});<\/code><\/pre>\n<pre><code class=\"language-csharp\">app.MapPost(\"\/upload\", async (IFormFileCollection myFiles) =&gt; { ... });<\/code><\/pre>\n<p>Using this feature with authentication requires anti-forgery support, which isn&#8217;t yet implemented. <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/38630\">Anti-forgery support for minimal APIs<\/a> is on our roadmap for .NET 7. Binding to <code>IFormFile<\/code> or <code>IFormFileCollection<\/code> when the request contains an <code>Authorization<\/code> 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.<\/p>\n<p>Thanks to @martincostello for contributing this feature.<\/p>\n<h4>Bind the request body as a <code>Stream<\/code> or <code>PipeReader<\/code><\/h4>\n<p>You can now bind the request body as a <code>Stream<\/code> or <code>PipeReader<\/code> 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:<\/p>\n<pre><code class=\"language-csharp\">app.MapPost(\"v1\/feeds\", async (QueueClient queueClient, Stream body, CancellationToken cancellationToken) =&gt;\r\n{\r\n    await queueClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken);\r\n    await queueClient.SendMessageAsync(await BinaryData.FromStreamAsync(body), cancellationToken: cancellationToken);\r\n});<\/code><\/pre>\n<p>When using the <code>Stream<\/code> or <code>PipeReader<\/code> there are a few things to take into consideration: <\/p>\n<ul>\n<li>When ingesting data, the <code>Stream<\/code> will be the same object as <code>HttpRequest.Body<\/code>.<\/li>\n<li>The request body isn&#8217;t buffered by default. After the body is read, it&#8217;s not rewindable (you can&#8217;t read the stream multiple times).<\/li>\n<li>The <code>Stream\/PipeReader<\/code> are not usable outside of the minimal action handler as the underlying buffers will be disposed and\/or reused.<\/li>\n<\/ul>\n<h3>JSON options configuration<\/h3>\n<p>We&#8217;re introducing a new and cleaner API, <code>ConfigureRouteHandlerJsonOptions<\/code>, to configure JSON options for minimal API endpoints. This new API avoids confusion with <code>Microsoft.AspNetCore.Mvc.JsonOptions<\/code>.<\/p>\n<pre><code class=\"language-csharp\">var builder = WebApplication.CreateBuilder(args);\r\nbuilder.Services.ConfigureRouteHandlerJsonOptions(options =&gt;\r\n{\r\n    \/\/Ignore Cycles\r\n    options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; \r\n});    <\/code><\/pre>\n<h2>SignalR client source generator<\/h2>\n<p>We&#8217;ve added a new client source generator for SignalR thanks to a contribution by @mehmetakbulut.<\/p>\n<p>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 <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/signalr\/hubs#strongly-typed-hubs\">strongly-typed SignalR hubs<\/a> on the client in place of the loosely-typed <code>.On(\"methodName\", ...)<\/code> methods. Similarly, your hub can implement an interface for its methods and the client can use that same interface to call the hub methods.<\/p>\n<p>To use the SignalR client source generator:<\/p>\n<ul>\n<li>Add a reference to the <a href=\"https:\/\/nuget.org\/packages\/Microsoft.AspNetCore.SignalR.Client.SourceGenerator\">Microsoft.AspNetCore.SignalR.Client.SourceGenerator<\/a> package.<\/li>\n<li>Add a <code>HubServerProxyAttribute<\/code> and <code>HubClientProxyAttribute<\/code> class to your project (this part of the design will likely change in future previews):<\/li>\n<\/ul>\n<pre><code class=\"language-csharp\">[AttributeUsage(AttributeTargets.Method)]\r\ninternal class HubServerProxyAttribute : Attribute\r\n{\r\n}\r\n\r\n[AttributeUsage(AttributeTargets.Method)]\r\ninternal class HubClientProxyAttribute : Attribute\r\n{\r\n}<\/code><\/pre>\n<ul>\n<li>Add a static partial class to your project and write static partial methods with the <code>[HubClientProxy]<\/code> and <code>[HubServerProxy]<\/code> attributes<\/li>\n<\/ul>\n<pre><code class=\"language-csharp\">internal static partial class MyCustomExtensions\r\n{\r\n    [HubClientProxy]\r\n    public static partial IDisposable ClientRegistration&lt;T&gt;(this HubConnection connection, T provider);\r\n\r\n    [HubServerProxy]\r\n    public static partial T ServerProxy&lt;T&gt;(this HubConnection connection);\r\n}<\/code><\/pre>\n<ul>\n<li>Use the partial methods from your code!<\/li>\n<\/ul>\n<pre><code class=\"language-csharp\">public interface IServerHub\r\n{\r\n    Task SendMessage(string message);\r\n    Task&lt;int&gt; Echo(int i);\r\n}\r\n\r\npublic interface IClient\r\n{\r\n    Task ReceiveMessage(string message);\r\n}\r\n\r\npublic class Client : IClient\r\n{\r\n    \/\/ Equivalent to HubConnection.On(\"ReceiveMessage\", (message) =&gt; {});\r\n    Task ReceiveMessage(string message)\r\n    {\r\n        return Task.CompletedTask;\r\n    }\r\n}\r\n\r\nHubConnection connection = new HubConnectionBuilder().WithUrl(\"...\").Build();\r\nvar stronglyTypedConnection = connection.ServerProxy&lt;IServerHub&gt;();\r\nvar registrations = connection.ClientRegistration&lt;IClient&gt;(new Client());\r\n\r\nawait stronglyTypedConnection.SendMessage(\"Hello world\");\r\nvar echo = await stronglyTypedConnection.Echo(10);<\/code><\/pre>\n<h2>Support for nullable models in MVC views and Razor Pages<\/h2>\n<p>We enabled defining a nullable page or view model to improve the experience when using null state checking with ASP.NET Core apps:<\/p>\n<pre><code class=\"language-razor\">@model Product?<\/code><\/pre>\n<h2>Use JSON property names in validation errors<\/h2>\n<p>When model validation produces a <code>ModelErrorDictionary<\/code> it will by default use the property name as the error key (<code>\"MyClass.PropertyName\"<\/code>). 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 <code>SystemTextJsonValidationMetadataProvider<\/code> (or <code>NewtonsoftJsonValidationMetadataProvider<\/code> when using Json.NET).<\/p>\n<pre><code class=\"language-csharp\">services.AddControllers(options =&gt;\r\n{\r\n    options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider())\r\n});<\/code><\/pre>\n<h2>Improved console output for <code>dotnet watch<\/code><\/h2>\n<p>We cleaned up the console output from <code>dotnet watch<\/code> to better align with the log out of ASP.NET Core and to stand out with \ud83d\ude2eemojis\ud83d\ude0d.<\/p>\n<p>Here&#8217;s an example of what the new output looks like:<\/p>\n<pre><code class=\"language-console\">C:BlazorApp&gt; dotnet watch\r\ndotnet watch \ud83d\udd25 Hot reload enabled. For a list of supported edits, see https:\/\/aka.ms\/dotnet\/hot-reload.\r\n  \ud83d\udca1 Press \"Ctrl + R\" to restart.\r\ndotnet watch \ud83d\udd27 Building...\r\n  Determining projects to restore...\r\n  All projects are up-to-date for restore.\r\n  You are using a preview version of .NET. See: https:\/\/aka.ms\/dotnet-support-policy\r\n  BlazorApp -&gt; C:UsersdarothDesktopBlazorAppbinDebugnet7.0BlazorApp.dll\r\ndotnet watch \ud83d\ude80 Started\r\ninfo: Microsoft.Hosting.Lifetime[14]\r\n      Now listening on: https:\/\/localhost:7148\r\ninfo: Microsoft.Hosting.Lifetime[14]\r\n      Now listening on: http:\/\/localhost:5041\r\ninfo: Microsoft.Hosting.Lifetime[0]\r\n      Application started. Press Ctrl+C to shut down.\r\ninfo: Microsoft.Hosting.Lifetime[0]\r\n      Hosting environment: Development\r\ninfo: Microsoft.Hosting.Lifetime[0]\r\n      Content root path: C:UsersdarothDesktopBlazorApp\r\ndotnet watch \u231a File changed: .PagesIndex.razor.\r\ndotnet watch \ud83d\udd25 Hot reload of changes succeeded.\r\ninfo: Microsoft.Hosting.Lifetime[0]\r\n      Application is shutting down...\r\ndotnet watch \ud83d\uded1 Shutdown requested. Press Ctrl+C again to force exit.<\/code><\/pre>\n<h2>Configure <code>dotnet watch<\/code> to always restart for rude edits<\/h2>\n<p>Configure <code>dotnet watch<\/code> to always restart without a prompt for rude edits (edits that can&#8217;t be hot reloaded) by setting the <code>DOTNET_WATCH_RESTART_ON_RUDE_EDIT<\/code> environment variable to <code>true<\/code>.<\/p>\n<h2>Inject services into custom validation attributes in Blazor<\/h2>\n<p>You can now inject services into custom validation attributes in Blazor. Blazor will setup the <code>ValidationContext<\/code> so it can be used as a service provider.<\/p>\n<pre><code class=\"language-csharp\">public class SaladChefValidatorAttribute : ValidationAttribute\r\n{\r\n    protected override ValidationResult IsValid(object value, ValidationContext validationContext)\r\n    {\r\n        var saladChef = validationContext.GetRequiredService&lt;SaladChef&gt;();\r\n        if (saladChef.ThingsYouCanPutInASalad.Contains(value.ToString()))\r\n        {\r\n            return ValidationResult.Success;\r\n        }\r\n        return new ValidationResult(\"You should not put that in a salad!\");\r\n    }\r\n}\r\n\r\n\/\/ Simple class configured as a service for dependency injection\r\npublic class SaladChef\r\n{\r\n    public string[] ThingsYouCanPutInASalad = { \"Strawberries\", \"Pineapple\", \"Honeydew\", \"Watermelon\", \"Grapes\" };\r\n}<\/code><\/pre>\n<p>Thank you @MariovanZeist for this contribution!<\/p>\n<h2>Faster header parsing and writing<\/h2>\n<p>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:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/pull\/38834\">HTTP\/2: Improve incoming header performance<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/pull\/39013\">HTTP\/3: Optimize validating and setting incoming headers<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/pull\/37538\">HTTP headers enumerator move directly to next<\/a><\/li>\n<\/ul>\n<h2>gRPC JSON transcoding<\/h2>\n<p>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:<\/p>\n<ul>\n<li>HTTP verbs<\/li>\n<li>URL parameter binding<\/li>\n<li>JSON requests\/responses<\/li>\n<\/ul>\n<p>Of course gRPC can continue to be used as well. RESTful APIs for your gRPC services. No duplication!<\/p>\n<p>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&#8217;t included with .NET 7 yet, but you can try out the existing experimental packages. For more information, see the <a href=\"https:\/\/github.com\/aspnet\/AspLabs\/tree\/main\/src\/GrpcHttpApi\">gRPC HTTP API getting started documentation<\/a>.<\/p>\n<h2>Give feedback<\/h2>\n<p>We hope you enjoy this preview release of ASP.NET Core in .NET 7 and that you&#8217;re as excited about about our roadmap for .NET 7 as we are! We&#8217;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 <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/new\">GitHub<\/a> and commenting on the <a href=\"https:\/\/aka.ms\/aspnet\/roadmap\">roadmap issue<\/a>.<\/p>\n<p>Thanks for trying out ASP.NET Core!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 7 Preview 1 is now available! Check out what&#8217;s new in ASP.NET Core in this update and learn about the roadmap for ASP.NET Core in .NET 7.<\/p>\n","protected":false},"author":417,"featured_media":38840,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,197,7509,7251],"tags":[7611],"class_list":["post-38839","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-aspnet","category-aspnetcore","category-blazor","tag-dotnet-7"],"acf":[],"blog_post_summary":"<p>.NET 7 Preview 1 is now available! Check out what&#8217;s new in ASP.NET Core in this update and learn about the roadmap for ASP.NET Core in .NET 7.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/38839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/417"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=38839"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/38839\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/38840"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=38839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=38839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=38839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}