ASP.NET Core 面接質問 25選:ミドルウェア、DI、Minimal APIs を完全攻略

ASP.NET Core の面接で頻出するミドルウェアパイプライン、依存性注入のライフタイム管理、Minimal APIs に関する25の質問と実践的なコード例を網羅的に解説します。

ASP.NET Core 面接対策:ミドルウェア、依存性注入、Minimal APIs

ASP.NET Core は、クロスプラットフォーム対応の高性能Webフレームワークとして、エンタープライズ開発の現場で広く採用されています。技術面接においては、ミドルウェアパイプライン、依存性注入(DI)、そして Minimal APIs に関する深い理解が求められます。本記事では、ASP.NET Core の面接で頻出する25の質問を厳選し、それぞれの回答と実践的なコード例を交えて解説します。

面接対策のポイント

ASP.NET Core の面接では、単なるAPI の使い方だけでなく、「なぜそのように設計されているのか」という設計思想の理解が重視されます。ミドルウェアの実行順序、DI のライフタイム管理、Minimal APIs と従来のコントローラーの使い分けなど、アーキテクチャレベルの判断力を示すことが合格への鍵となります。

ミドルウェアパイプライン(質問 1〜6)

1. ASP.NET Core のミドルウェアとは何ですか。また、リクエストパイプラインにおける役割を説明してください

ミドルウェアとは、HTTPリクエストとレスポンスを処理するためにパイプラインに組み込まれるソフトウェアコンポーネントです。各ミドルウェアは、リクエストを次のコンポーネントに渡す前後で処理を行い、必要に応じてパイプラインを短絡(short-circuit)させることもできます。ロギング、認証、例外処理、CORS など、横断的関心事を分離するための仕組みとして設計されています。

2. ミドルウェアの実行順序はなぜ重要ですか。正しい登録順序を示してください

ミドルウェアは登録された順序で実行されるため、順序を誤ると認証前にエンドポイントが実行されるなどのセキュリティ上の問題が発生します。以下は推奨される登録順序です。

Program.cs — Middleware order matterscsharp
var app = builder.Build();

app.UseExceptionHandler("/error");   // 1. Outermost: catches all exceptions
app.UseHsts();                        // 2. HTTP Strict Transport Security
app.UseHttpsRedirection();            // 3. Redirect HTTP to HTTPS
app.UseStaticFiles();                 // 4. Serve static files (short-circuits if found)
app.UseRouting();                     // 5. Match request to endpoint
app.UseAuthentication();              // 6. Identify the user
app.UseAuthorization();               // 7. Check permissions
app.MapControllers();                 // 8. Execute the matched endpoint

app.Run();

UseExceptionHandler を最も外側に配置することで、パイプライン内のあらゆる例外をキャッチできます。UseStaticFiles は静的ファイルが見つかった場合にパイプラインを短絡させるため、認証の前に配置するのが一般的です。

3. カスタムミドルウェアはどのように実装しますか

カスタムミドルウェアはクラスベースで実装します。コンストラクタで RequestDelegate を受け取り、InvokeAsync メソッドで処理を記述します。以下はリクエストの処理時間を計測するミドルウェアの例です。

RequestTimingMiddleware.cscsharp
public class RequestTimingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestTimingMiddleware> _logger;

    public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        
        await _next(context);  // Call the next middleware
        
        stopwatch.Stop();
        _logger.LogInformation(
            "Request {Method} {Path} completed in {Elapsed}ms",
            context.Request.Method,
            context.Request.Path,
            stopwatch.ElapsedMilliseconds);
    }
}

// Registration in Program.cs
app.UseMiddleware<RequestTimingMiddleware>();

コンストラクタで DI コンテナからサービスを注入できる点が、インラインミドルウェア(app.Use())との大きな違いです。

4. app.Use()app.Run()app.Map() の違いは何ですか

app.Use() はパイプラインにインラインミドルウェアを追加し、next() を呼び出して次のミドルウェアに処理を渡します。app.Run() はターミナルミドルウェアとして機能し、パイプラインを終了させます。app.Map() はリクエストパスに基づいてパイプラインを分岐させ、特定のパスに対して異なるミドルウェアチェーンを構成できます。

5. ミドルウェアの短絡(short-circuiting)とは何ですか

短絡とは、ミドルウェアが next() デリゲートを呼び出さずにレスポンスを返すことで、後続のミドルウェアの実行をスキップする動作です。例えば、UseStaticFiles は要求されたファイルが存在する場合にパイプラインを短絡させます。認証ミドルウェアも、無効なトークンを検出した場合に 401 レスポンスを返して短絡させることがあります。

6. 例外処理ミドルウェアとフィルターの使い分けを説明してください

例外処理ミドルウェア(UseExceptionHandler)はパイプライン全体の例外をキャッチするグローバルなセーフティネットです。一方、例外フィルター(IExceptionFilter)はMVCパイプライン内でのみ動作し、アクションの実行中に発生した例外を処理します。一般的には、ミドルウェアでグローバルな例外処理を行い、フィルターでドメイン固有のエラーハンドリングを行うという組み合わせが推奨されます。

依存性注入(質問 7〜12)

7. ASP.NET Core における DI の3つのライフタイムを説明してください

ASP.NET Core の DI コンテナは3つのサービスライフタイムを提供します。Transient は要求されるたびに新しいインスタンスを生成します。Scoped はHTTPリクエストごとに1つのインスタンスを共有します。Singleton はアプリケーションのライフタイム全体で1つのインスタンスを維持します。ライフタイムの選択はメモリ使用量やスレッドセーフティに直接影響するため、慎重な判断が求められます。

8. Scoped サービスはどのような場合に使用しますか

Scoped サービスは、1つのHTTPリクエスト内でステートを共有する必要がある場合に適しています。典型的な例として、Entity Framework Core の DbContext があります。DbContext は変更追跡やトランザクション管理のためにリクエストスコープで共有される必要がありますが、リクエスト間では分離されるべきです。

9. キャプティブ依存性(Captive Dependency)問題とは何ですか

キャプティブ依存性に注意

Singleton サービスが Scoped や Transient のサービスに依存すると、短いライフタイムのサービスが Singleton によって「捕捉」され、想定より長く生存してしまいます。これにより、メモリリークやスレッドセーフティの問題が発生する可能性があります。開発環境では ValidateScopes を有効にして、この問題を起動時に検出することが重要です。

Program.cs — Captive dependency detectioncsharp
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddSingleton<ICacheService, CacheService>(); // CacheService takes IUserRepository

// This throws at startup in Development when ValidateScopes is enabled:
builder.Host.UseDefaultServiceProvider(options =>
{
    options.ValidateScopes = true;    // Catches captive dependencies
    options.ValidateOnBuild = true;   // Validates all registrations at startup
});

ValidateOnBuildtrue にすると、未登録のサービスへの依存も起動時に検出できるため、本番環境でのランタイムエラーを未然に防ぐことができます。

10. .NET 8 で導入されたキー付きサービス(Keyed Services)とは何ですか

キー付きサービスは、同一インターフェースの複数の実装をキー(文字列)で区別して登録・解決する機能です。従来は手動でファクトリパターンを実装する必要がありましたが、キー付きサービスにより DI コンテナがネイティブにサポートするようになりました。

Program.cs — Registering keyed servicescsharp
builder.Services.AddKeyedSingleton<INotificationService, EmailNotification>("email");
builder.Services.AddKeyedSingleton<INotificationService, SmsNotification>("sms");
builder.Services.AddKeyedSingleton<INotificationService, PushNotification>("push");

// Resolving in a controller or service
public class OrderService
{
    public OrderService(
        [FromKeyedServices("email")] INotificationService emailService,
        [FromKeyedServices("sms")] INotificationService smsService)
    {
        // Each parameter gets its specific implementation
    }
}

面接では、キー付きサービスが導入される以前の解決策(ファクトリパターンや IEnumerable<T> の利用)との比較を説明できると、より深い理解を示すことができます。

11. デコレーターパターンを DI で実装する方法を説明してください

ASP.NET Core の組み込み DI コンテナはデコレーターを直接サポートしていませんが、ファクトリ登録を使うことで実現できます。以下は、キャッシュ機能をデコレーターとして追加する例です。

csharp
// Decorating IProductService with caching
builder.Services.AddScoped<ProductService>();
builder.Services.AddScoped<IProductService>(sp =>
{
    var inner = sp.GetRequiredService<ProductService>();
    var cache = sp.GetRequiredService<IMemoryCache>();
    return new CachedProductService(inner, cache);
});

この手法により、元のサービスのコードを変更することなく、キャッシュやロギングなどの横断的関心事を追加できます。より複雑なデコレーターチェーンが必要な場合は、Scrutor などのライブラリの活用が推奨されます。

12. DI コンテナに同一インターフェースの複数の実装を登録した場合、どのように解決されますか

同一インターフェースに対して複数の実装を登録した場合、最後に登録されたものがデフォルトとして解決されます。すべての実装を取得するには IEnumerable<T> を注入します。特定の実装を選択するにはキー付きサービス、またはファクトリパターンを使用します。

.NETの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

Minimal APIs(質問 13〜18)

13. Minimal APIs とは何ですか。従来のコントローラーとの違いを説明してください

Minimal APIs は、ASP.NET Core 6 で導入された軽量なAPI構築アプローチです。コントローラークラスやアクションフィルターの仕組みを使わず、Program.cs やエンドポイントクラスに直接ルートハンドラーを定義します。ボイラープレートコードが大幅に削減される一方、コントローラーが提供するモデルバインディングやフィルターパイプラインの一部機能は利用できません。マイクロサービスや小規模APIに適しています。

14. Minimal APIs でエンドポイントをグループ化し、整理する方法を示してください

MapGroup を使用することで、共通のプレフィックスや設定を持つエンドポイントをグループ化できます。以下は、機能ごとにエンドポイントを整理する推奨パターンです。

Features/Products/ProductEndpoints.cscsharp
public static class ProductEndpoints
{
    public static void MapProductEndpoints(this WebApplication app)
    {
        var group = app.MapGroup("/api/products")
            .WithTags("Products")
            .RequireAuthorization();

        group.MapGet("/", GetAll);
        group.MapGet("/{id:int}", GetById);
        group.MapPost("/", Create);
    }

    private static async Task<IResult> GetAll(
        IProductService productService,    // Auto-resolved from DI
        CancellationToken cancellationToken)
    {
        var products = await productService.GetAllAsync(cancellationToken);
        return TypedResults.Ok(products);
    }

    private static async Task<IResult> GetById(
        int id,
        IProductService productService)
    {
        var product = await productService.GetByIdAsync(id);
        return product is null
            ? TypedResults.NotFound()
            : TypedResults.Ok(product);
    }

    private static async Task<IResult> Create(
        CreateProductRequest request,
        IProductService productService)
    {
        var product = await productService.CreateAsync(request);
        return TypedResults.Created($"/api/products/{product.Id}", product);
    }
}

// Program.cs — Clean registration
app.MapProductEndpoints();

拡張メソッドとして定義することで、Program.cs をクリーンに保ちつつ、機能ごとのモジュール化を実現できます。

15. TypedResultsResults の違いは何ですか

ResultsIResult を返す静的ヘルパーメソッドを提供しますが、返却型は IResult のままです。TypedResults は具体的な型(Ok<T>, NotFound など)を返すため、OpenAPI ドキュメントの自動生成やコンパイル時の型チェックに優れています。Minimal APIs では TypedResults の使用が推奨されます。

16. Minimal APIs でバリデーションを実装する方法を説明してください

Minimal APIs にはコントローラーのような自動モデルバリデーション機構がないため、エンドポイントフィルターや FluentValidation などのライブラリを使用します。エンドポイントフィルター(AddEndpointFilter)を使えば、ハンドラーの実行前にバリデーションロジックを挿入できます。共通のバリデーションフィルターを作成し、MapGroup に適用することで、DRY原則を守りながらバリデーションを一元管理できます。

17. Minimal APIs で DI はどのように機能しますか

Minimal APIs のハンドラーパラメータは、DI コンテナから自動的に解決されます。パラメータの型がDIに登録されたサービスと一致する場合、フレームワークが自動的にインスタンスを注入します。ルートパラメータ、クエリ文字列、リクエストボディとの区別は、パラメータの型と属性([FromQuery], [FromBody] など)に基づいて行われます。

18. Minimal APIs でミドルウェアとエンドポイントフィルターをどのように使い分けますか

ミドルウェアはパイプライン全体に適用されるグローバルな処理に適しています。エンドポイントフィルターは特定のエンドポイントやグループに対してのみ適用される処理に使用します。例えば、リクエストロギングはミドルウェアで、入力バリデーションはエンドポイントフィルターで実装するのが適切です。

応用・総合問題(質問 19〜25)

19. Options パターンとは何ですか。設定管理のベストプラクティスを説明してください

Options パターンは、appsettings.json などの設定値を強い型付けのクラスにバインドし、DI を通じて提供する仕組みです。IOptions<T>IOptionsSnapshot<T>IOptionsMonitor<T> の3種類があり、それぞれ Singleton、Scoped(リクエストごとに再読み込み)、リアルタイム変更通知という特性を持ちます。設定クラスにはデータアノテーションによるバリデーションも適用でき、ValidateOnStart で起動時検証が可能です。

20. ヘルスチェックの実装方法と、その活用場面を説明してください

ASP.NET Core のヘルスチェック機能(AddHealthChecks)は、アプリケーションとその依存サービス(データベース、外部API、メッセージキューなど)の稼働状況を監視するためのエンドポイントを提供します。Kubernetes の Liveness Probe や Readiness Probe、ロードバランサーのヘルスチェックに利用されます。カスタムヘルスチェックは IHealthCheck インターフェースを実装して作成し、HealthyDegradedUnhealthy のいずれかのステータスを返します。

21. Rate Limiting ミドルウェアの仕組みと設定方法を説明してください

.NET 7 で導入された組み込みの Rate Limiting ミドルウェアは、Fixed Window、Sliding Window、Token Bucket、Concurrency の4つのアルゴリズムをサポートしています。AddRateLimiter でポリシーを定義し、UseRateLimiter でパイプラインに追加します。Minimal APIs では RequireRateLimiting("policyName") でエンドポイントごとにポリシーを適用できます。ユーザー単位やIPアドレス単位での制限も、パーティションキーを指定することで実現可能です。

22. Output Caching と Response Caching の違いは何ですか

Response Caching は HTTP キャッシュヘッダー(Cache-Control)に基づいてクライアントや中間プロキシでのキャッシュを制御する仕組みです。Output Caching(.NET 7 以降)はサーバーサイドでレスポンスをキャッシュし、後続のリクエストに対してパイプラインの大部分をバイパスして高速にレスポンスを返します。Output Caching はタグベースの無効化やカスタムポリシーに対応しており、より柔軟なキャッシュ戦略を実現できます。

23. ミドルウェアの単体テストはどのように実装しますか

ミドルウェアのテストでは、DefaultHttpContext を使用してHTTPコンテキストを構築し、ターミナルデリゲートとして Task.CompletedTask を返すラムダを渡します。以下は RequestTimingMiddleware のテスト例です。

RequestTimingMiddlewareTests.cscsharp
[Fact]
public async Task Middleware_LogsElapsedTime()
{
    var logger = new FakeLogger<RequestTimingMiddleware>();
    var middleware = new RequestTimingMiddleware(
        next: (ctx) => Task.CompletedTask,  // Terminal delegate
        logger: logger);

    var context = new DefaultHttpContext();
    context.Request.Method = "GET";
    context.Request.Path = "/api/products";

    await middleware.InvokeAsync(context);

    Assert.Single(logger.LogEntries);
    Assert.Contains("completed in", logger.LogEntries[0].Message);
}

FakeLogger のようなテスト用のロガー実装を使うことで、ログ出力を検証できます。統合テストでは WebApplicationFactory を使用して、パイプライン全体の動作を検証することも可能です。

24. Minimal APIs と従来のコントローラーを同一プロジェクトで共存させることは可能ですか

共存は完全にサポートされています。Program.csapp.MapControllers() と Minimal API のエンドポイント登録を併記するだけで実現できます。段階的なマイグレーション戦略として、新しいエンドポイントを Minimal APIs で追加しつつ、既存のコントローラーをそのまま維持するアプローチが実践的です。ルーティングの競合に注意し、OpenAPI ドキュメントではタグを使ってグループ分けすることが推奨されます。

25. ASP.NET Core アプリケーションのパフォーマンス最適化において、ミドルウェアと DI の観点から重要なポイントは何ですか

パフォーマンス最適化においては、以下の観点が重要です。ミドルウェアの順序を最適化し、短絡可能な処理(静的ファイル、ヘルスチェック)を早い段階に配置すること。DI のライフタイムを適切に選択し、不要な Transient 登録を避けて Scoped や Singleton を活用すること。IServiceProvider を直接使用するサービスロケーターパターンを避け、コンストラクタインジェクションを徹底すること。非同期ミドルウェアでは async/await を正しく使用し、不要なスレッドプールの消費を防ぐこと。これらの要素を総合的に考慮することで、高スループットかつ低レイテンシのアプリケーションを実現できます。

面接準備の進め方

25問すべてを暗記する必要はありません。重要なのは、ミドルウェアパイプラインの設計原則、DI ライフタイムのトレードオフ、Minimal APIs の適用判断といった「なぜ」の部分を自分の言葉で説明できることです。実際にコードを書いて動作を確認し、各コンセプトの関連性を体系的に理解することが、面接での自信につながります。

まとめ

ASP.NET Core の面接で成功するためには、以下のポイントを押さえておくことが重要です。

  • ミドルウェアパイプライン: 実行順序の重要性、短絡の仕組み、カスタムミドルウェアの実装パターンを理解する
  • 依存性注入: 3つのライフタイム(Transient、Scoped、Singleton)の使い分け、キャプティブ依存性の問題と検出方法、キー付きサービスやデコレーターパターンを把握する
  • Minimal APIs: コントローラーとの違い、MapGroup によるエンドポイントの整理、TypedResults の利点、エンドポイントフィルターの活用方法を習得する
  • テスト: DefaultHttpContextWebApplicationFactory を活用したミドルウェアとエンドポイントのテスト手法を身につける
  • 応用: Options パターン、ヘルスチェック、Rate Limiting、Output Caching など、本番運用に不可欠な機能を網羅する

これらの知識は、単に面接を突破するためだけでなく、堅牢でスケーラブルなASP.NET Core アプリケーションを設計・構築するための基盤となります。SharpSkill の模擬面接シミュレーターフラッシュカードを活用して、実践的な問答形式でこれらの概念を繰り返し確認することをお勧めします。体系的な準備が、面接当日の確かな自信を生み出します。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

タグ

#asp.net core
#interview
#middleware
#dependency injection
#minimal apis
#.net

共有

関連記事