Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
vermeir_nico_introducing_net_6_getting_started_with_blazor_m.pdf
Скачиваний:
19
Добавлен:
26.06.2023
Размер:
11.64 Mб
Скачать

Chapter 7 ASP.NET Core

Minimal APIs

As mentioned in the introduction of this chapter, minimal APIs are Microsoft’s answer to fast REST API development as can be found in frameworks like NodeJS, where you can put the entire API surface in one file. Looking at minimal APIs, we effectively have one file containing everything. From application setup to the actual endpoints, and even better: Swagger and Swagger UI know how to interpret this new style of .NET REST APIs.

I have implemented the same book API in minimal API; a quick count of lines of code shows the following result:

•\

Controller-based API: 78 lines of code

•\

Minimal API: 57 lines of code

Lines of code are the bootstrap logic in Program.cs and the book endpoints, so not counting any models or services. That is 25% less code. But less code is not always better. Listing 7-27 shows the first part of Program.cs that sets up the application.

Listing 7-27.  Bootstrapping a minimal API project

var builder = WebApplication.CreateBuilder(args);

//Add services to the container.

//Learn more about configuring Swagger/OpenAPI at https://aka.ms/ aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen();

builder.Services.AddSingleton<IBookCatalogService, BookCatalogService>();

var app = builder.Build();

// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment())

{

app.UseSwagger();

app.UseSwaggerUI();

}

app.UseHttpsRedirection();

215

Chapter 7 ASP.NET Core

Almost identical to controller-based APIs, except for the builder.Services. AddControllers call, we have no controllers in a minimal API, so no need to add them to the ServiceCollection.

The next part of Program.cs is the endpoints; after defining the endpoints, there is a small line of code that says app.Run(). These few characters make the API launch and be useful. Do not remove this call or nothing will work.

Listing 7-28 shows the action to fetch an array of books.

Listing 7-28.  Creating a GET request in a minimal API

app.MapGet("/api/book", async ([FromServices] IBookCatalogService bookCatalogService) =>

{

Book[] books = await bookCatalogService.FetchBookCatalog(); return Results.Ok(books);

})

.WithName("GetBooks")

.Produces<Book[]>();

Creating API endpoints is done on the WebApplication object that is also used for configuring the application as seen in Listing 7-27. WebApplication has different Map methods depending on the type of HTTP verb you want to create an endpoint for. In this example, we are creating a GET request. The first parameter of the method is the path on which the endpoint is reachable. The second parameter is the delegate that is called whenever the endpoint is called. This delegate can be placed in a separate method, but I personally prefer the anonymous method style. In a minimal API, we

can’t do constructor injection since we don’t have a constructor. Instead, we can use the FromServices attribute to decorate a parameter in the delegate. The ServiceCollection will inject the instance right there. In controller-based APIs, we could use helper methods like Ok and NotFound from the controller base class. We don’t have a base class in a minimal API, but we can use the static class Results that exposes the same methods. Finally we have some configuration methods; these are mostly used for generating the OpenAPI JSON and for Swagger UI. WithName sets a name for this endpoint. Produces sets the possible HTTP status codes and the type of data we can expect. In this example, we are not explicitly setting a status code; HTTP 200 will be used as default.

216

Chapter 7 ASP.NET Core

Listing 7-29.  Parameters in a minimal API endpoint

app.MapGet("/api/book/{id}", async ([FromServices] IBookCatalogService bookCatalogService, int id) =>

{

Book? book = await bookCatalogService.FetchBookById(id);

if (book == null)

{

return Results.NotFound(id);

}

return Results.Ok(book);

})

.WithName("GetBookById")

.Produces<Book>()

.Produces((int)HttpStatusCode.NotFound);

The API call for fetching book details looks very similar. The major difference is that we are expecting a parameter, the book ID, to be passed in. Passing parameters is done by setting a placeholder between curly braces in the route and creating a method parameter in the delegate with the same name as the placeholder. The framework will take care of mapping the passed in parameter to the .NET parameter, very similar to the way routing works as we have seen in ASP.NET MVC. This example also shows defining two possible results. The method can either produce a book with HTTP 200 – OK or it can produce an empty result with HTTP 404 – Not Found.

Listing 7-30.  Posting data to a minimal API

app.MapPost("/api/book", async ([FromServices] IBookCatalogService bookCatalogService, [FromBody] Book book) =>

{

await bookCatalogService.AddBook(book);

return Results.Created($"/api/book/{book.Id}", book);

})

.WithName("AddBook")

.Produces<Book>((int)HttpStatusCode.Created);

217

Chapter 7 ASP.NET Core

Creating a POST endpoint is very similar; we just use the MapPost method instead of MapGet. Using the FromBody attribute, we can get the posted HTTP form data as a .NET object passed into the delegate. The return type is HTTP 201 – Created.

If you like this style of API programming but you don’t want a large Program.cs once all your endpoints are defined, you can use extension methods to split your endpoint definitions in separate files.

Listing 7-31.  Extension methods for defining book endpoints

public static class BookEndpoints

{

public static void MapBookEndpoints(this WebApplication app)

{

app.MapGet("/api/book", async ([FromServices] IBookCatalogService bookCatalogService) =>

{

Book[] books = await bookCatalogService.FetchBookCatalog(); return Results.Ok(books);

})

.WithName("GetBooks")

.Produces<Book[]>();

}

}

I have only added one endpoint in Listing 7-31 for brevity. The full method with the three endpoints can be found on the book’s GitHub page. With this extension method in place, we can shorten Program.cs by calling app.MapBookEndpoints instead of defining all endpoints there.

218