Request model defined for an endpoint is bound with [AsParameters] attribute (except for ServiceEndpoints). Any field under request model can be bound from route, query, body, form, etc. with corresponding [From...] attribute (see Minimal APIs Parameter Binding for more information).
The following sample demonstrates route and body parameter binding.
public record UpdateBookRequest(Guid Id, [FromBody] UpdateBookRequestBody Body);
public record UpdateBookRequestBody(string Title, string Author, decimal Price);
public record UpdateBookResponse(Guid Id, string Title, string Author, decimal Price);
internal class UpdateBookRequestValidator : AbstractValidator<UpdateBookRequest>
{
public UpdateBookRequestValidator()
{
RuleFor(x => x.Id).NotEmpty();
RuleFor(x => x.Body.Title).NotEmpty();
RuleFor(x => x.Body.Author).NotEmpty();
RuleFor(x => x.Body.Price).GreaterThan(0);
}
}
internal class UpdateBook(ServiceDbContext db)
: WebResultEndpoint<UpdateBookRequest, UpdateBookResponse>
{
protected override void Configure(
EndpointConfigurationBuilder builder,
EndpointConfigurationContext configurationContext)
{
builder.MapPut("/books/{Id}")
.Produces<UpdateBookResponse>();
}
protected override async Task<WebResult<UpdateBookResponse>> HandleAsync(
UpdateBookRequest req,
CancellationToken ct)
{
var entity = await db.Books.FirstOrDefaultAsync(b => b.Id == req.Id, ct);
if (entity is null)
{
return FailureResult.NotFound();
}
entity.Title = req.Body.Title;
entity.Author = req.Body.Author;
entity.Price = req.Body.Price;
var updated = await db.SaveChangesAsync(ct);
return updated > 0 ?
Result.Ok(new UpdateBookResponse(
Id: req.Id,
Title: req.Body.Title,
Author: req.Body.Author,
Price: req.Body.Price))
: FailureResult.NotFound();
}
}The following sample demonstrates route and form parameter binding.
public record UploadBookRequest(string Title, [FromForm] string Author, IFormFile BookFile);
public record UploadBookResponse(string FileName, long FileSize);
internal class UploadBookRequestValidator : AbstractValidator<UploadBookRequest>
{
public UploadBookRequestValidator()
{
RuleFor(x => x.Title).NotEmpty();
RuleFor(x => x.Author).NotEmpty();
RuleFor(x => x.BookFile).NotEmpty();
}
}
internal class UploadBook
: WebResultEndpoint<UploadBookRequest, UploadBookResponse>
{
protected override void Configure(
EndpointConfigurationBuilder builder,
EndpointConfigurationContext configurationContext)
{
builder.MapPost("/books/upload/{Title}")
.DisableAntiforgery()
.Produces<UploadBookResponse>();
}
protected override Task<WebResult<UploadBookResponse>> HandleAsync(
UploadBookRequest req,
CancellationToken ct)
{
return Task.FromResult(
WebResults.FromResult(
new UploadBookResponse(
req.BookFile.FileName,
req.BookFile.Length)));
}
}Note: The
DisableAntiforgerymethod is used to disable CSRF protection for this endpoint. The default behavior of ASP.NET Core is to require an antiforgery token for Minimal API endpoints that bind a parameter from the form viaIFormFileorIFormFileCollectionand an exception is thrown at startup if the anti-forgery middleware isn't registered for an API that defines these input types. You should be cautious when disabling CSRF protection and ensure that your application is secure against CSRF attacks.