ASP.NET Core Minimal APIs 2026幎å®å šã¬ã€ãïŒã¢ãŒããã¯ãã£ãããã©ãŒãã³ã¹ã颿¥å¯Ÿç
ASP.NET Core Minimal APIsã®èšèšååãããã©ãŒãã³ã¹æé©åãäŸåæ§æ³šå ¥ããšã³ããã€ã³ãæŽçã®å®è·µç解説ã2026å¹Žã®æè¡é¢æ¥ã§é »åºãã質åãšåçäŸãåé²ã

ASP.NET Core Minimal APIsã¯ãåŸæ¥ã®MVCã³ã³ãããŒã©ãŒã§å¿ èŠãšãããŠããå®åçãªã³ãŒããå€§å¹ ã«åæžããHTTPãšã³ããã€ã³ãã®æ§ç¯ãã·ã³ãã«ãã€å¹ççã«è¡ãããã®ã¢ãããŒãã§ãã.NET 6ã§å°å ¥ãããŠä»¥æ¥ã.NET 8ã.NET 9ããããŠææ°ã®.NET 10ãžãšé²åãç¶ãããã€ã¯ããµãŒãã¹ããµãŒããŒã¬ã¹é¢æ°ã軜éãªWebãµãŒãã¹ã®éçºã«ãããŠæ¬çªç°å¢ã§äœ¿çšå¯èœãªéžæè¢ãšããŠç¢ºç«ãããŠããŸãã
Minimal APIsã¯Program.csã«çŽæ¥å®çŸ©ããããããã¬ãã«ã®ã«ãŒããã³ãã©ãŒã䜿çšããŸããäžæ¹ãMVCã³ã³ãããŒã©ãŒã¯ã¯ã©ã¹å®çŸ©ã屿§ãèŠçŽããŒã¹ã®ã«ãŒãã£ã³ã°ãå¿ èŠã§ãã20ãšã³ããã€ã³ãæªæºã®ã·ã³ãã«ãªCRUDæäœããã€ã¯ããµãŒãã¹ã§ã¯ãMinimal APIsã«ããã³ãŒãéã40ã60%åæžã§ããããšãå€ãã§ãã
Minimal APIã®ã¢ãŒããã¯ãã£ãšãªã¯ãšã¹ããã€ãã©ã€ã³
ASP.NET Coreã®ãªã¯ãšã¹ããã€ãã©ã€ã³ã¯ãHTTPãªã¯ãšã¹ããããã«ãŠã§ã¢ã³ã³ããŒãã³ããéããŠåŠçãããšã³ããã€ã³ããã³ãã©ãŒã«å°éãããŸããMinimal APIsã¯ãã®ãã€ãã©ã€ã³ãšã·ãŒã ã¬ã¹ã«çµ±åããªãããã«ãŒãå®çŸ©ã®ããã®ãã宣èšçãªæ§æãæäŸããŸãã
var builder = WebApplication.CreateBuilder(args);
// äŸåæ§æ³šå
¥ã®ããã®ãµãŒãã¹ç»é²
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<IProductRepository, ProductRepository>();
var app = builder.Build();
// ããã«ãŠã§ã¢ãã€ãã©ã€ã³ã®èšå®
app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseAuthorization();
// Minimal APIãšã³ããã€ã³ãå®çŸ©
app.MapGet("/products", async (IProductRepository repo) =>
Results.Ok(await repo.GetAllAsync()));
app.MapGet("/products/{id:int}", async (int id, IProductRepository repo) =>
await repo.GetByIdAsync(id) is Product product
? Results.Ok(product)
: Results.NotFound());
app.Run();ãã®ãã¿ãŒã³ã§ã¯ãäŸåæ§æ³šå
¥ã³ã³ãããžã®å®å
šãªã¢ã¯ã»ã¹ãç¶æããªãããã«ãŒãå®çŸ©ãéäžç®¡çã§ããŸããIProductRepositoryãã©ã¡ãŒã¿ã¯ããã³ãã©ãŒããªã²ãŒããžã®ã³ã³ã¹ãã©ã¯ã¿ã¬ã¹æ³šå
¥ã瀺ããŠããŸãã
ã«ãŒãã°ã«ãŒããšãšã³ããã€ã³ãã®æŽç
ã¢ããªã±ãŒã·ã§ã³ãæé·ããã«ã€ããŠããšã³ããã€ã³ãã®æŽçãéèŠã«ãªããŸãã.NET 7ã§å°å ¥ãããã«ãŒãã°ã«ãŒãã¯ãæå°éã®ã¢ãããŒããç¶æããªãããåå空éãšå ±æèšå®ãæäŸããŸãã
public static class ProductEndpoints
{
public static void MapProductEndpoints(this WebApplication app)
{
var group = app.MapGroup("/api/products")
.WithTags("Products")
.RequireAuthorization();
group.MapGet("/", GetAllProducts);
group.MapGet("/{id:int}", GetProductById);
group.MapPost("/", CreateProduct)
.Accepts<CreateProductRequest>("application/json")
.Produces<Product>(StatusCodes.Status201Created);
group.MapPut("/{id:int}", UpdateProduct);
group.MapDelete("/{id:int}", DeleteProduct)
.RequireAuthorization("AdminOnly");
}
private static async Task<IResult> GetAllProducts(
IProductRepository repo,
CancellationToken ct)
{
var products = await repo.GetAllAsync(ct);
return Results.Ok(products);
}
private static async Task<IResult> GetProductById(
int id,
IProductRepository repo,
CancellationToken ct)
{
var product = await repo.GetByIdAsync(id, ct);
return product is not null
? Results.Ok(product)
: Results.NotFound();
}
private static async Task<IResult> CreateProduct(
CreateProductRequest request,
IProductRepository repo,
CancellationToken ct)
{
var product = await repo.CreateAsync(request, ct);
return Results.Created($"/api/products/{product.Id}", product);
}
}ãã®ã¢ãããŒãã«ãããProgram.csã¯ã¯ãªãŒã³ã«ä¿ããããšã³ããã€ã³ãé¢é£ã®ããžãã¯ã¯å°çšãã¡ã€ã«ã«åé¢ãããŸãã
ããã©ãŒãã³ã¹æé©åãã¯ããã¯
Minimal APIsã¯ããã®è»œéãªèšèšã«ããåªããããã©ãŒãã³ã¹ãæäŸããŸããããããªãæé©åãå¯èœã§ãã
// éåæã¹ããªãŒãã³ã°ã«ãã倧éããŒã¿ã®å¹ççãªåŠç
app.MapGet("/products/stream", async (IProductRepository repo) =>
{
async IAsyncEnumerable<Product> StreamProducts()
{
await foreach (var product in repo.GetAllAsyncStream())
{
yield return product;
}
}
return Results.Ok(StreamProducts());
});
// ã¬ã¹ãã³ã¹ãã£ãã·ã³ã°ã®æŽ»çš
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromMinutes(5)));
options.AddPolicy("ProductCache", builder =>
builder.Expire(TimeSpan.FromMinutes(10))
.Tag("products"));
});
app.MapGet("/products", async (IProductRepository repo) =>
Results.Ok(await repo.GetAllAsync()))
.CacheOutput("ProductCache");
// ãã£ãã·ã¥ç¡å¹å
app.MapPost("/products", async (
CreateProductRequest request,
IProductRepository repo,
IOutputCacheStore cache) =>
{
var product = await repo.CreateAsync(request);
await cache.EvictByTagAsync("products", default);
return Results.Created($"/api/products/{product.Id}", product);
});åºåãã£ãã·ã³ã°ã¯.NET 7ã§å°å ¥ããããšã³ããã€ã³ãã¬ãã«ã§ã®ã¬ã¹ãã³ã¹ãã£ãã·ã³ã°ãç°¡åã«èšå®ã§ããŸããã¿ã°ããŒã¹ã®ç¡å¹åã«ãããé¢é£ãããã£ãã·ã¥ãäžæ¬ã§ã¯ãªã¢ããããšãå¯èœã§ãã
ããªããŒã·ã§ã³ãšãšã©ãŒãã³ããªã³ã°
å ç¢ãªAPIã«ã¯é©åãªããªããŒã·ã§ã³ãšãšã©ãŒãã³ããªã³ã°ãäžå¯æ¬ ã§ãã
// FluentValidationã®çµ±å
builder.Services.AddScoped<IValidator<CreateProductRequest>, CreateProductValidator>();
public class CreateProductValidator : AbstractValidator<CreateProductRequest>
{
public CreateProductValidator()
{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("åååã¯å¿
é ã§ã")
.MaximumLength(100).WithMessage("åååã¯100æå以å
ã§å
¥åããŠãã ãã");
RuleFor(x => x.Price)
.GreaterThan(0).WithMessage("äŸ¡æ Œã¯0ãã倧ããå€ãå
¥åããŠãã ãã");
}
}
// ããªããŒã·ã§ã³ãã£ã«ã¿ãŒã®äœæ
public class ValidationFilter<T> : IEndpointFilter where T : class
{
private readonly IValidator<T> _validator;
public ValidationFilter(IValidator<T> validator)
{
_validator = validator;
}
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext context,
EndpointFilterDelegate next)
{
var argument = context.Arguments
.OfType<T>()
.FirstOrDefault();
if (argument is null)
return Results.BadRequest("ãªã¯ãšã¹ãããã£ãå¿
èŠã§ã");
var result = await _validator.ValidateAsync(argument);
if (!result.IsValid)
{
var errors = result.Errors
.GroupBy(e => e.PropertyName)
.ToDictionary(
g => g.Key,
g => g.Select(e => e.ErrorMessage).ToArray());
return Results.ValidationProblem(errors);
}
return await next(context);
}
}
// ãã£ã«ã¿ãŒã®é©çš
group.MapPost("/", CreateProduct)
.AddEndpointFilter<ValidationFilter<CreateProductRequest>>();èªèšŒãšèªå¯ã®å®è£
Minimal APIsã§ã¯ãèªèšŒãšèªå¯ããã«ãŒãšã³ããªæ§æã§èšå®ã§ããŸãã
// JWTèªèšŒã®èšå®
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
};
});
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin"))
.AddPolicy("CanManageProducts", policy =>
policy.RequireClaim("permission", "products:write"));
// ãšã³ããã€ã³ãã§ã®èªå¯é©çš
var adminGroup = app.MapGroup("/api/admin")
.RequireAuthorization("AdminOnly");
adminGroup.MapGet("/users", async (IUserRepository repo) =>
Results.Ok(await repo.GetAllAsync()));
adminGroup.MapDelete("/users/{id}", async (int id, IUserRepository repo) =>
{
await repo.DeleteAsync(id);
return Results.NoContent();
});OpenAPI/Swaggerããã¥ã¡ã³ãçæ
APIããã¥ã¡ã³ãã¯éçºè äœéšã«ãããŠéèŠãªèŠçŽ ã§ãã
// Swaggerèšå®
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Products API",
Version = "v1",
Description = "åå管çAPI"
});
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT"
});
});
// ãšã³ããã€ã³ãã®ããã¥ã¡ã³ã匷å
group.MapGet("/{id:int}", GetProductById)
.WithName("GetProduct")
.WithSummary("IDã«ããååååŸ")
.WithDescription("æå®ãããIDã®ååãååŸããŸããååšããªãå Žåã¯404ãè¿ããŸãã")
.Produces<Product>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);æè¡é¢æ¥ã§ããèããã質åãšåç
Minimal APIsã«é¢ãã颿¥ã§ã¯ã以äžã®ãããªè³ªåãé »åºããŸãã
Q: Minimal APIsãšMVCã³ã³ãããŒã©ãŒã®éžæåºæºã¯ïŒ
Minimal APIsã¯ãã·ã³ãã«ãªCRUDæäœããã€ã¯ããµãŒãã¹ãå°æ°ã®ãšã³ããã€ã³ããæã€APIã«é©ããŠããŸããäžæ¹ãMVCã³ã³ãããŒã©ãŒã¯ãè€éãªãã¥ãŒè«çã倿°ã®ãšã³ããã€ã³ããæã€å€§èŠæš¡APIãããŒã ãMVCãã¿ãŒã³ã«æ £ããŠããå Žåã«é©ããŠããŸãã
Q: ãšã³ããã€ã³ããã£ã«ã¿ãŒãšããã«ãŠã§ã¢ã®éãã¯ïŒ
ããã«ãŠã§ã¢ã¯ãªã¯ãšã¹ããã€ãã©ã€ã³å šäœã«é©çšããããã¹ãŠã®ãªã¯ãšã¹ããåŠçããŸãããšã³ããã€ã³ããã£ã«ã¿ãŒã¯ç¹å®ã®ãšã³ããã€ã³ãã«ã®ã¿é©çšããããããã现ããå¶åŸ¡ãå¯èœã§ããããªããŒã·ã§ã³ããã®ã³ã°ãªã©ããšã³ããã€ã³ãåºæã®åŠçã«ã¯ãã£ã«ã¿ãŒãé©ããŠããŸãã
Q: Minimal APIsã§ã®ãã¹ãæ¹æ³ã¯ïŒ
// WebApplicationFactoryã䜿çšããçµ±åãã¹ã
public class ProductApiTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public ProductApiTests(WebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task GetProducts_ReturnsOk()
{
var response = await _client.GetAsync("/api/products");
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
}.NETã®é¢æ¥å¯Ÿçã¯ã§ããŠããŸããïŒ
ã€ã³ã¿ã©ã¯ãã£ããªã·ãã¥ã¬ãŒã¿ãŒãflashcardsãæè¡ãã¹ãã§ç·Žç¿ããŸãããã
ãŸãšã
ASP.NET Core Minimal APIsã¯ã軜éã§é«éãªAPIãæ§ç¯ããããã®åŒ·åãªããŒã«ã§ããã«ãŒãã°ã«ãŒãã«ããæŽçããšã³ããã€ã³ããã£ã«ã¿ãŒã«ããæšªæçé¢å¿äºã®åŠçãåºåãã£ãã·ã³ã°ã«ããããã©ãŒãã³ã¹æé©åãªã©ãå®éçšã«å¿ èŠãªæ©èœãæã£ãŠããŸãã
æè¡é¢æ¥ã§ã¯ãMinimal APIsãšMVCã³ã³ãããŒã©ãŒã®äœ¿ãåããäŸåæ§æ³šå ¥ã®ä»çµã¿ãããã«ãŠã§ã¢ãšãã£ã«ã¿ãŒã®éãã«ã€ããŠã®çè§£ãåãããŸããã³ãŒãäŸãéããŠå®è·µçãªç¥èã身ã«ã€ããé©åãªãŠãŒã¹ã±ãŒã¹ã説æã§ããããã«ããŠããããšãéèŠã§ãã
å ±æ
é¢é£èšäº

.NET 10ïŒ2026幎çïŒïŒæ°æ©èœãNative AOTã颿¥å¯Ÿçå®å šã¬ã€ã
.NET 10ã®æ°æ©èœã培åºè§£èª¬ãNative AOTã®æ¬çªç°å¢å¯Ÿå¿ãC# 14ã®æ¡åŒµã¡ã³ããŒãfieldããŒã¯ãŒããEF Core 10ã®ååä»ãã¯ãšãªãã£ã«ã¿ãŒãªã©ã颿¥ã§å·®ãã€ããç¥èãç¶²çŸ ããŸãã

.NET MAUI 2026幎å®å šã¬ã€ãïŒã¯ãã¹ãã©ãããã©ãŒã éçºãšé¢æ¥é »åºè³ªå
.NET MAUI 10ã«ããã¯ãã¹ãã©ãããã©ãŒã ã¢ããªéçºã®æé ãHandlersãMVVMãHybridWebViewãSafeAreaEdgesãšå ±ã«è§£èª¬ãã2026å¹Žã®æè¡é¢æ¥ã§é »åºãã質åãšåçãäœç³»çã«ãŸãšããŠããŸãã

ASP.NET Core 颿¥è³ªå 25éžïŒããã«ãŠã§ã¢ãDIãMinimal APIs ãå®å šæ»ç¥
ASP.NET Core ã®é¢æ¥ã§é »åºããããã«ãŠã§ã¢ãã€ãã©ã€ã³ãäŸåæ§æ³šå ¥ã®ã©ã€ãã¿ã€ã 管çãMinimal APIs ã«é¢ãã25ã®è³ªåãšå®è·µçãªã³ãŒãäŸãç¶²çŸ çã«è§£èª¬ããŸãã