Bagikan melalui


Pengelogan HTTP di ASP.NET Core

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Peringatan

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Pengelogan HTTP adalah middleware yang mencatat informasi tentang permintaan HTTP masuk dan respons HTTP. Pengelogan HTTP menyediakan log dari:

  • Informasi permintaan HTTP
  • Properti umum
  • Header
  • Isi
  • Informasi respons HTTP

Pengelogan HTTP dapat:

  • Catat semua permintaan dan respons atau hanya permintaan dan respons yang memenuhi kriteria tertentu.
  • Pilih bagian mana dari permintaan dan respons yang dicatat.
  • Memungkinkan Anda untuk meredaksi informasi sensitif dari log.

Pengelogan HTTP dapat mengurangi performa aplikasi, terutama saat mencatat isi permintaan dan respons. Pertimbangkan dampak performa saat memilih bidang yang akan dicatat. Uji dampak performa dari properti pengelogan yang dipilih.

Peringatan

Pengelogan HTTP berpotensi mencatat informasi pengidentifikasi pribadi (PII). Pertimbangkan risikonya dan hindari pengelogan informasi sensitif.

Aktifkan pengelogan HTTP

Pengelogan HTTP diaktifkan dengan memanggil AddHttpLogging dan UseHttpLogging, seperti yang ditunjukkan dalam contoh berikut:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(o => { });

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Hello World!");

app.Run();

Lambda kosong dalam contoh panggilan AddHttpLogging sebelumnya menambahkan middleware dengan konfigurasi default. Secara default, log pengelogan HTTP mencatat properti umum seperti jalur, kode status, dan header untuk permintaan dan respons.

Tambahkan baris berikut ke file appsettings.Development.json di tingkat "LogLevel": { sehingga log HTTP ditampilkan:

 "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"

Dengan konfigurasi default, permintaan dan respons dicatat sebagai sepasang pesan yang mirip dengan contoh berikut:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Protocol: HTTP/2
      Method: GET
      Scheme: https
      PathBase:
      Path: /
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
      Host: localhost:52941
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.61
      Accept-Encoding: gzip, deflate, br
      Accept-Language: en-US,en;q=0.9
      Upgrade-Insecure-Requests: [Redacted]
      sec-ch-ua: [Redacted]
      sec-ch-ua-mobile: [Redacted]
      sec-ch-ua-platform: [Redacted]
      sec-fetch-site: [Redacted]
      sec-fetch-mode: [Redacted]
      sec-fetch-user: [Redacted]
      sec-fetch-dest: [Redacted]
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      StatusCode: 200
      Content-Type: text/plain; charset=utf-8
      Date: Tue, 24 Oct 2023 02:03:53 GMT
      Server: Kestrel

Opsi pengelogan HTTP

Untuk mengonfigurasi opsi global untuk middleware pengelogan HTTP, panggil AddHttpLogging di Program.cs, menggunakan lambda untuk mengonfigurasi HttpLoggingOptions.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Catatan

Dalam sampel sebelumnya dan sampel berikut, UseHttpLogging dipanggil setelah UseStaticFiles, sehingga pengelogan HTTP tidak diaktifkan untuk file statis. Untuk mengaktifkan pengelogan HTTP file statis, panggil UseHttpLogging sebelum UseStaticFiles.

LoggingFields

HttpLoggingOptions.LoggingFields adalah bendera enum yang mengonfigurasi bagian tertentu dari permintaan dan respons terhadap log. HttpLoggingOptions.LoggingFields defaultnya adalah RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders dan ResponseHeaders

RequestHeaders dan ResponseHeaders merupakan set header HTTP yang dicatat. Nilai header hanya dicatat untuk nama header yang ada di koleksi ini. Kode berikut menambahkan sec-ch-ua ke RequestHeaders, sehingga nilai sec-ch-ua header dicatat. Dan menambahkan MyResponseHeader ke ResponseHeaders, sehingga nilai MyResponseHeader header dicatat. Jika baris ini dihapus, nilai header ini adalah [Redacted].

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

MediaTypeOptions

MediaTypeOptions menyediakan konfigurasi untuk memilih pengodean mana yang akan digunakan untuk jenis media tertentu.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Pendekatan ini juga dapat digunakan untuk mengaktifkan pengelogan untuk data yang tidak dicatat secara default (misalnya, data formulir, yang mungkin memiliki jenis media seperti application/x-www-form-urlencoded atau multipart/form-data).

Metode MediaTypeOptions

RequestBodyLogLimit dan ResponseBodyLogLimit

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

CombineLogs

Pengaturan CombineLogs untuk true mengonfigurasi middleware untuk mengonsolidasikan semua log yang diaktifkan untuk permintaan dan respons ke dalam satu log di akhir. Ini termasuk permintaan, isi permintaan, respons, isi respons, dan durasi.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
    logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Konfigurasi khusus titik akhir

Untuk konfigurasi khusus titik akhir di aplikasi API minimal, WithHttpLogging metode ekstensi tersedia. Contoh berikut menunjukkan cara mengonfigurasi pengelogan HTTP untuk satu titik akhir:

app.MapGet("/response", () => "Hello World! (logging response)")
    .WithHttpLogging(HttpLoggingFields.ResponsePropertiesAndHeaders);

Untuk konfigurasi khusus titik akhir di aplikasi yang menggunakan pengontrol, [HttpLogging] atribut tersedia. Atribut juga dapat digunakan dalam aplikasi API minimal, seperti yang ditunjukkan dalam contoh berikut:

app.MapGet("/duration", [HttpLogging(loggingFields: HttpLoggingFields.Duration)]
    () => "Hello World! (logging duration)");

IHttpLoggingInterceptor

IHttpLoggingInterceptor adalah antarmuka untuk layanan yang dapat diimplementasikan untuk menangani panggilan balik per permintaan dan per respons untuk menyesuaikan detail apa yang dicatat. Setiap pengaturan log khusus titik akhir diterapkan terlebih dahulu dan kemudian dapat ditimpa dalam panggilan balik ini. Implementasi dapat:

  • Periksa permintaan atau respons.
  • Aktifkan atau nonaktifkan .HttpLoggingFields
  • Sesuaikan berapa banyak isi permintaan atau respons yang dicatat.
  • Tambahkan bidang kustom ke log.

IHttpLoggingInterceptor Daftarkan implementasi dengan memanggil AddHttpLoggingInterceptor<T> di Program.cs. Jika beberapa IHttpLoggingInterceptor instans terdaftar, instans tersebut dijalankan dalam urutan terdaftar.

Contoh berikut menunjukkan cara mendaftarkan IHttpLoggingInterceptor implementasi:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.Duration;
});
builder.Services.AddHttpLoggingInterceptor<SampleHttpLoggingInterceptor>();

Contoh berikut adalah IHttpLoggingInterceptor implementasi yang:

  • Memeriksa metode permintaan dan menonaktifkan pengelogan untuk permintaan POST.
  • Untuk permintaan non-POST:
    • Meredaksi jalur permintaan, header permintaan, dan header respons.
    • Menambahkan bidang kustom dan nilai bidang ke log permintaan dan respons.
using Microsoft.AspNetCore.HttpLogging;

namespace HttpLoggingSample;

internal sealed class SampleHttpLoggingInterceptor : IHttpLoggingInterceptor
{
    public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
    {
        if (logContext.HttpContext.Request.Method == "POST")
        {
            // Don't log anything if the request is a POST.
            logContext.LoggingFields = HttpLoggingFields.None;
        }

        // Don't enrich if we're not going to log any part of the request.
        if (!logContext.IsAnyEnabled(HttpLoggingFields.Request))
        {
            return default;
        }

        if (logContext.TryDisable(HttpLoggingFields.RequestPath))
        {
            RedactPath(logContext);
        }

        if (logContext.TryDisable(HttpLoggingFields.RequestHeaders))
        {
            RedactRequestHeaders(logContext);
        }

        EnrichRequest(logContext);

        return default;
    }

    public ValueTask OnResponseAsync(HttpLoggingInterceptorContext logContext)
    {
        // Don't enrich if we're not going to log any part of the response
        if (!logContext.IsAnyEnabled(HttpLoggingFields.Response))
        {
            return default;
        }

        if (logContext.TryDisable(HttpLoggingFields.ResponseHeaders))
        {
            RedactResponseHeaders(logContext);
        }

        EnrichResponse(logContext);

        return default;
    }

    private void RedactPath(HttpLoggingInterceptorContext logContext)
    {
        logContext.AddParameter(nameof(logContext.HttpContext.Request.Path), "RedactedPath");
    }

    private void RedactRequestHeaders(HttpLoggingInterceptorContext logContext)
    {
        foreach (var header in logContext.HttpContext.Request.Headers)
        {
            logContext.AddParameter(header.Key, "RedactedHeader");
        }
    }

    private void EnrichRequest(HttpLoggingInterceptorContext logContext)
    {
        logContext.AddParameter("RequestEnrichment", "Stuff");
    }

    private void RedactResponseHeaders(HttpLoggingInterceptorContext logContext)
    {
        foreach (var header in logContext.HttpContext.Response.Headers)
        {
            logContext.AddParameter(header.Key, "RedactedHeader");
        }
    }

    private void EnrichResponse(HttpLoggingInterceptorContext logContext)
    {
        logContext.AddParameter("ResponseEnrichment", "Stuff");
    }
}

Dengan pencegat ini, permintaan POST tidak menghasilkan log apa pun bahkan jika pengelogan HTTP dikonfigurasi untuk mencatat HttpLoggingFields.All. Permintaan GET menghasilkan log yang mirip dengan contoh berikut:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Path: RedactedPath
      Accept: RedactedHeader
      Host: RedactedHeader
      User-Agent: RedactedHeader
      Accept-Encoding: RedactedHeader
      Accept-Language: RedactedHeader
      Upgrade-Insecure-Requests: RedactedHeader
      sec-ch-ua: RedactedHeader
      sec-ch-ua-mobile: RedactedHeader
      sec-ch-ua-platform: RedactedHeader
      sec-fetch-site: RedactedHeader
      sec-fetch-mode: RedactedHeader
      sec-fetch-user: RedactedHeader
      sec-fetch-dest: RedactedHeader
      RequestEnrichment: Stuff
      Protocol: HTTP/2
      Method: GET
      Scheme: https
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      Content-Type: RedactedHeader
      MyResponseHeader: RedactedHeader
      ResponseEnrichment: Stuff
      StatusCode: 200
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[4]
      ResponseBody: Hello World!
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[8]
      Duration: 2.2778ms

Urutan konfigurasi pengelogan prioritas

Daftar berikut menunjukkan urutan prioritas untuk konfigurasi pengelogan:

  1. Konfigurasi global dari HttpLoggingOptions, diatur dengan memanggil AddHttpLogging.
  2. Konfigurasi khusus titik akhir [HttpLogging] dari atribut atau WithHttpLogging metode ekstensi mengambil alih konfigurasi global.
  3. IHttpLoggingInterceptor dipanggil dengan hasil dan dapat memodifikasi konfigurasi lebih lanjut per permintaan.

Pengelogan HTTP adalah middleware yang mencatat informasi tentang permintaan HTTP dan respons HTTP. Pengelogan HTTP menyediakan log dari:

  • Informasi permintaan HTTP
  • Properti umum
  • Header
  • Isi
  • Informasi respons HTTP

Pengelogan HTTP berguna dalam beberapa skenario untuk:

  • Mencatat informasi tentang permintaan dan respons yang masuk.
  • Memfilter bagian mana dari permintaan dan respons yang dicatat.
  • Memfilter header mana yang akan dicatat.

Pengelogan HTTP dapat mengurangi performa aplikasi, terutama saat melakukan pengelogan badan permintaan dan respons. Pertimbangkan dampak performa saat memilih bidang yang akan dicatat. Uji dampak performa dari properti pengelogan yang dipilih.

Peringatan

Pengelogan HTTP berpotensi dapat mencatat informasi pengidentifikasi pribadi (PII). Pertimbangkan risikonya dan hindari pengelogan informasi sensitif.

Mengaktifkan pengelogan HTTP

Pengelogan HTTP diaktifkan dengan UseHttpLogging, yang menambahkan middleware pengelogan HTTP.

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Hello World!");

app.Run();

Secara default, Pengelogan HTTP mencatat properti umum seperti jalur, kode status, dan header untuk permintaan dan respons. Tambahkan baris berikut ke file appsettings.Development.json di tingkat "LogLevel": { sehingga log HTTP ditampilkan:

 "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"

Outputnya dicatat sebagai satu pesan di LogLevel.Information.

Sampel output permintaan

Opsi Pengelogan HTTP

Untuk mengonfigurasi middleware pengelogan HTTP, panggil AddHttpLogging di Program.cs.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;

});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Catatan

Dalam sampel sebelumnya dan sampel berikut, UseHttpLogging dipanggil setelah UseStaticFiles, sehingga pengelogan HTTP tidak diaktifkan untuk file statis. Untuk mengaktifkan pengelogan HTTP file statis, panggil UseHttpLogging sebelum UseStaticFiles.

LoggingFields

HttpLoggingOptions.LoggingFields adalah bendera enum yang mengonfigurasi bagian tertentu dari permintaan dan respons terhadap log. HttpLoggingOptions.LoggingFields defaultnya adalah RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.

RequestHeaders

Headers adalah sekumpulan Header Permintaan HTTP yang diizinkan untuk dicatat. Nilai header hanya dicatat untuk nama header yang ada di koleksi ini. Kode berikut mencatat "sec-ch-ua" header permintaan. Jika logging.RequestHeaders.Add("sec-ch-ua"); dihapus, nilai header permintaan "sec-ch-ua" akan dihapus. Kode yang disorot berikut memanggil HttpLoggingOptions.RequestHeaders dan HttpLoggingOptions.ResponseHeaders :

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;

});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

MediaTypeOptions

MediaTypeOptions menyediakan konfigurasi untuk memilih pengodean mana yang akan digunakan untuk jenis media tertentu.

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;

});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

Pendekatan ini juga dapat digunakan untuk mengaktifkan pengelogan untuk data yang tidak dicatat secara default (misalnya data formulir, yang mungkin memiliki jenis media seperti application/x-www-form-urlencoded atau multipart/form-data).

Metode MediaTypeOptions

RequestBodyLogLimit dan ResponseBodyLogLimit

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("MyResponseHeader");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;

});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging(); 

app.Use(async (context, next) =>
{
    context.Response.Headers["MyResponseHeader"] =
        new string[] { "My Response Header Value" };

    await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();