Search code examples
c#asp.net-corehttpclientrefit

How do I log HTTP requests when using Refit in .net core?


I want to use Refit to query an API. First thing, I need to obtain an access token which is a POST request returning a status 400. I have no idea what causes the problem, thus I would like to see the actual raw request that is being sent to the API. Unfortunately, I haven't managed to get this to work and most likely I will need it more often during development.

I found this handsome logger and thought I could use it in my program.cs as a DelegatingHandler like this:

builder.Services.AddRefitClient<IVismaConnect>().ConfigureHttpClient(config =>
{
    config.BaseAddress = new Uri("https://ENDPOINT");
    config.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "SOMECREDENTIALSTHATEVENTUALLYWILLBEREADFROMACONFIGFILE");
    config.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/x-www-form-urlencoded");
}).AddHttpMessageHandler<HttpLoggingHandler>();

Now I get a fancy error message telling me that "No service for type ...HttpLoggingHandler has been registered":

enter image description here

So I googled a bit and found a hint that I need to register the HttpLoggingHandler as a Singleton, thus I put this line right above my code in program.cs (tried it below as well, did not make any difference):

builder.Services.AddTransient<HttpLoggingHandler>();

Now I get another exception saying that "The 'InnerHandler' property must be null. 'DelegatingHandler' instances provided to 'HttpMessageHandlerBuilder' must not be reused or cached":

enter image description here

After more googling I found some documentation about HTTP logging in ASP.NET core from Microsoft.

That did not change anything, the exception still popped up, so I tried just the MS suggestion without the HttpLoggingHandler I tried earlier, but that did not generate any output whatsoever.

Any suggestions on how to properly log the raw requests Refit sends to a server would be highly appreciated. See below the full program.cs file:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
});

// Add services to the container.

builder.Services.AddTransient<HttpLoggingHandler>();

builder.Services.AddRefitClient<IVismaConnect>().ConfigureHttpClient(config =>
{
    config.BaseAddress = new Uri("https://ENDPOINT");
    config.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "SOMECREDENTIALSTHATEVENTUALLYWILLBEREADFROMACONFIGFILE");
    config.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/x-www-form-urlencoded");
}); //.AddHttpMessageHandler<HttpLoggingHandler>();

builder.Configuration
    .SetBasePath(Environment.CurrentDirectory)
    .AddJsonFile("appsettings.json", true)
    .AddUserSecrets(Assembly.GetExecutingAssembly(), true)
    .AddEnvironmentVariables()
    .Build();


ConfigurationManager configuration = builder.Configuration;
C.Instance.SetConfiguration(configuration);


builder.Logging.ClearProviders();
builder.Logging.AddConfiguration(configuration).AddConsole().AddDebug().AddSimpleConsole();

IWebHostEnvironment environment = builder.Environment;


// Add services to the container.
builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      builder =>
                      {
                          builder.WithOrigins("*").AllowAnyMethod().AllowAnyHeader();
                      });
});

builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpLogging();

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

await next();
});

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");

app.MapFallbackToFile("index.html"); ;

app.Run();

Solution

  • Removing constructor from your custom handler should do it. The current implementation of the handler always initialise itself with the parameterised constructor, which makes InnerHandler not null.