Minimal APIs at a glance in .NET 6
David Fowler doesn't have a blog. I think the psychic weight of having a blog would stress him out. Fortunately, David's 'blog' is actually hidden in his prolific GitHub commits and GitHub Gists.
David has been quietly creating an amazing piece of documentation for Minimal APIs in .NET 6. At some point when it's released we'll work with David to get everything promoted to formal documentation, but as far as I'm concerned if he is slapping the keyboard anywhere and it shows up anywhere with a URL then I'm happy with the result!
Let's explore a bit here and I encourage you to head over to the main Gist here.
To start, we see how easy it is to make a .NET 6 (minimal) app to say Hello World over HTTP on localhost:5000/5001
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World");
app.Run();
Lovely. It's basically nothing. Can I do more HTTP Verbs? Yes.
app.MapGet("/", () => "This is a GET");
app.MapPost("/", () => "This is a POST");
app.MapPut("/", () => "This is a PUT");
app.MapDelete("/", () => "This is a DELETE");
What about other verbs? More than one?
app.MapMethods("/options-or-head", new [] { "OPTIONS", "HEAD" }, () => "This is an options or head request ");
Lambda expressions, not objects, are our "atoms" that we build molecules with in this world. They are the building blocks.
app.MapGet("/", () => "This is an inline lambda");
var handler = () => "This is a lambda variable";
app.MapGet("/", handler)
But it's just a function, so you can organize things however you want!
var handler = new HelloHandler();
app.MapGet("/", handler.Hello);
class HelloHandler
{
public string Hello()
{
return "Hello World";
}
}
You can capture route parameters as part of the route pattern definition.
app.MapGet("/users/{userId}/books/{bookId}", (int userId, int bookId) => $"The user id is {userId} and book id is {bookId}");
Route constraints are influence the matching behavior of a route. See how this is in order of specificity:
app.MapGet("/todos/{id:int}", (int id) => db.Todos.Find(id));
app.MapGet("/todos/{text}", (string text) => db.Todos.Where(t => t.Text.Contains(text));
app.MapGet("/posts/{slug:regex(^[a-z0-9_-]+$)}", (string slug) => $"Post {slug}");
Attributes can be used to explicitly declare where parameters should be bound from! So you can pick and choose from all over!
using Microsoft.AspNetCore.Mvc;
app.MapGet("/{id}", ([FromRoute]int id,
[FromQuery(Name = "p")]int page,
[FromServices]Service service,
[FromHeader(Name = "Content-Type")]string contentType) => { });
I can customize the response:
app.MapGet("/todos/{id}", (int id, TodoDb db) =>
db.Todos.Find(id) is Todo todo
? Results.Ok(todo)
: Results.NotFound()
);
Here's a cool example of taking a file upload and writing it to a local file. Nice and clean.
app.MapGet("/upload", async (HttpRequest req) =>
{
if (!req.HasFormContentType)
{
return Results.BadRequest();
}
var form = await req.ReadFormAsync();
var file = form.Files["file"];
if (file is null)
{
return Results.BadRequest();
}
var uploads = Path.Combine(uploadsPath, file.FileName);
await using var fileStream = File.OpenWrite(uploads);
await using var uploadStream = file.OpenReadStream();
await uploadStream.CopyToAsync(fileStream);
return Results.NoContent();
})
.Accepts<IFormFile>("multipart/form-data");
Go check out this great (and growing) online resource to learn about .NET 6 minimal APIs.
Sponsor: YugabyteDB is a distributed SQL database designed for resilience and scale. It is 100% open source, PostgreSQL-compatible, enterprise-grade, and runs across all clouds. Sign up and get a free t-shirt.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
var handler = () => "This is a lambda variable";
valid C# syntax? Doesn't handler have to be explicitly typed as Action, Func<string>, or other compatible delegate type?
Since C# 10. See https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md for more information.
I can see that it can make some code samples on stackoverflow more concise, but I don't really see how this is a serious productivity improvement in the real world (and I would assume Microsoft view this as a significant improvement, since it is getting much publicity)?
Thanks :)
Can minimal api structure be used to setup a SignalR Hub StreamAsync() and MassagePack() for C#.
I've not tried it myself, but I spotted an example on David Fowler's twitter stream just this morning.
(I dream of being that prolific....)
@Anders Olesen
Does anyone know what the motivation is for adding this new syntax? At least to me, I doubt that the bit of additional boiler plate code required to set up something similar in asp.net core 5 was a big problem for people building real applications?
My opinion is this, things change for the sake of change. A lot of tools have gotten better so there isn't a lot of efficiency lost in the current syntax. We might as well learn F# since that's where C# seems to be heading.
Fine with your permission allow me to grab your feed to keep up to date with forthcoming post. Thanks a million and please keep up the enjoyable work.
Comments are closed.