Scoped service creating two different instances for a request - c#

I'm fairly new to Asp.Net core 6 and am working on an GraphQL API that receives a bearer token in the request. The API then invokes another Web API and passes the same bearer token in the header. Below is what my code looks like-
Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<HeaderHandler>();
builder.Services.AddScoped<TokenContainer>();
//builder.Services.AddScoped<IFooGateway, FooGateway>();
builder.Services.AddHttpClient<IFooGateway, FooGateway>((c) =>
{
c.BaseAddress = new Uri(builder.Configuration["FooApiUrl"]);
})
.AddHttpMessageHandler<HeaderHandler>();
builder.Services.AddTransient<GraphApiService>();
var app = builder.Build();
app.UseMiddleware<HeaderMiddleware>();
app.MapGraphQL();
app.Run();
HeaderMiddleware.cs
public class HeaderMiddleware
{
//TokenContainer _tokenContainer;
private readonly RequestDelegate _requestDelegate;
public HeaderMiddleware()
{
}
public HeaderMiddleware(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
//_tokenContainer = tokenContainer;
}
public async Task InvokeAsync(HttpContext context, TokenContainer tokenContainer)
{
var header = context.Request.Headers.Authorization;
tokenContainer.SetToken(header);
await _requestDelegate(context);
}
TokenContainer.cs:
public class TokenContainer
{
public string BearerToken { get; private set; }
public void SetToken(string token) => BearerToken = token;
}
HeaderHandler.cs:
public class HeaderHandler : DelegatingHandler
{
TokenContainer _tokenContainer;
public HeaderHandler()
{
}
public HeaderHandler(TokenContainer tokenContainer)
{
_tokenContainer = tokenContainer;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// for every request sent via the http client, intercept & add the bearer token header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", _tokenContainer.BearerToken);
// continue with request pipeline
return await base.SendAsync(request, cancellationToken);
}
}
FooGateway.cs:
public class FooGateway : IFooGateway
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly string _context = String.Empty;
public FooGateway(HttpClient httpClient, IConfiguration configuration)
{
_configuration = configuration;
_context = configuration["FooContext"];
_httpClient = httpClient;
}
public void DoSomething()
{
_httpClient.PostAsync("/blabla");
}
}
So, the idea was that the bearer token for every incoming request will be stored in a class called TokenContainer and the HttpHandler will add it to all the outgoing requests.
However, what is happening is that the token is stored in the TokenContainer but the HeaderHandler gets a different instance of TokenContainer in its constructor with its BearerToken property set to null.
Can someone please explain why the same instance of TokenContainer from the middleware is not being passed into the HeaderHandler?

The issue you are seeing is because the lifetime of the HttpMessageHandler is not the same as the lifetime of the request: usually, the same handler will be reused across many requests and be controlled separately on expiration timers and such.
You should not expect that a service injected into your message handler will be the same object that is injected outside it when it is registered as scoped.
https://andrewlock.net/understanding-scopes-with-ihttpclientfactory-message-handlers/#scope-duration-in-ihttpclientfactory
As the article suggests, to use the same scoped instance as you do outside the handler, you have to rely on IHttpContextAccessor to access the current HttpContext and fetch the service from there. So your handler implementation would look something like this:
public class HeaderHandler : DelegatingHandler
{
IHttpContextAccessor _httpContextAccessor;
public HeaderHandler()
{
}
public HeaderHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var tokenContainer = _httpContextAccessor
.HttpContext
.RequestServices
.GetRequiredService<TokenContainer>();
// for every request sent via the http client, intercept & add the bearer token header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", tokenContainer.BearerToken);
// continue with request pipeline
return await base.SendAsync(request, cancellationToken);
}
}
This should make sure that the TokenContainer instance is the same across your current request and http calls.
Remember that to add this functionality you need to add the accessor like this:
services.AddHttpContextAccessor();

Related

Blazor WebAssembly blocks WebApi AllowAnonymous

I have created an Blazor WebAssembly project and want to provide a WebAPI with one public available function.
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class SystemEvalApiController : ControllerBase
{
public SystemEvalApiController(AppDbContext context, IMapper mapper)
{...}
[Route("LatestEvals")]
[AllowAnonymous]
public ActionResult LatestEvals()
that is my Api controller and I should be able to call it with:
SystemEvalPublicViewModel = await Http
.GetFromJsonAsync<SystemEvalPublicViewModel>(
HttpService.BuildUrl("api/SystemEvalApi/LatestEvals"));
When i am not logged into any account. But instead I get this error:
info: System.Net.Http.HttpClient.JPB.BorannRemapping.ServerAPI.LogicalHandler[100]
Start processing HTTP request GET https://localhost:44330/api/SystemEvalApi/LatestEvals
blazor.webassembly.js:1 info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
It looks like the "DefaultAuthorizationService" does not recognize the Anonymous attribute but I cannot find the point where it fails directly.
How do I declare an WebAPI function to be accessable from the HttpClient without Login.
Microsoft.AspNetCore.Components.WebAssembly.Server 3.2.0.-rc1.20223.4
Edit:
Here is the declaration for ClientServices:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddHttpClient("JPB.BorannRemapping.ServerAPI", client =>
{
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
})
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("JPB.BorannRemapping.ServerAPI"));
builder.Services.AddTransient(e => new HttpService(e.GetService<HttpClient>()));
builder.Services.AddApiAuthorization();
builder.Services.AddBlazoredLocalStorage();
await builder.Build().RunAsync();
So each time you acquire an HttpClient it use the BaseAddressAuthorizationMessageHandler which try to authentify the request. But it this case your request should not be authentified, so you can make something like :
Registration
builder.Services.AddHttpClient("JPB.BorannRemapping.ServerAPI.Anonymous", client =>
{
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
});
Usage
#inject IHttpClientFactory _factory
#code {
...
var httpClient = _factory.CreateClient("JPB.BorannRemapping.ServerAPI.Anonymous");
var httpService = new HttpService(httpClient);
SystemEvalPublicViewModel = await httpClient
.GetFromJsonAsync<SystemEvalPublicViewModel>(
httpService.BuildUrl("api/SystemEvalApi/LatestEvals"));
}
Building on the answer from #agua from mars.
Registration in Program.cs
You could add 2 named HttpClient to the services collection (the first for authenticated calls the second for anonymous):
builder.Services.AddHttpClient("YourProject.ServerAPI",
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddHttpClient("YourProject.ServerAPI.Anonymous",
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("YourProject.ServerAPI"));
//Register a new service for getting an Anonymous HttpClient
builder.Services.AddScoped<IHttpAnonymousClientFactory, HttpAnonymousClientFactory>();
Add new Interface and Implementation for Dependency Injection:
public interface IHttpAnonymousClientFactory
{
HttpClient HttpClient { get; }
}
public class HttpAnonymousClientFactory : IHttpAnonymousClientFactory
{
private readonly IHttpClientFactory httpClientFactory;
public HttpAnonymousClientFactory(IHttpClientFactory httpClientFactory)
{
this.httpClientFactory = httpClientFactory;
}
public HttpClient HttpClient => httpClientFactory.CreateClient("YourProject.ServerAPI.Anonymous");
}
Usage in Razor Component (for Anonymous HttpClient)
[Inject]
private IHttpAnonymousClientFactory httpAnonymousClientFactory { get; set; }
private MyViewModel myModel;
protected override async Task OnInitializedAsync()
{
myModel = await httpAnonymousClientFactory.HttpClient.GetFromJsonAsync<MyViewModel>($"api/mycontroller/myendpoint");
}
Usage in Razor Component (for Authenticated HttpClient)
[Inject]
private HttpClient httpClient { get; set; }
private MyOtherViewModel myModel;
protected override async Task OnInitializedAsync()
{
myModel = await httpClient.GetFromJsonAsync<MyOtherViewModel>($"api/mycontroller/mysecureendpoint");
}

Call API during Claims Transformation

I am adding claims transformation into my Blazor (server-side) application. I am creating an HTTP Web API service using DI. Below is the start up code.
services.AddHttpClient<IAPIClient, APIClient>();
services.AddScoped<IClaimsTransformation, ClaimsLoader>();
I would like to then use claims transformation to call this Web API once I am authenticated. Which looks like:
public class ClaimsLoader : IClaimsTransformation
{
private readonly HttpClient _apiClient;
private readonly IHttpContextAccessor _httpAccessor;
public ClaimsLoader(IHttpContextAccessor httpAccessor, HttpClient apiClient)
{
_httpAccessor = httpAccessor;
_apiClient = apiClient;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var identity = (ClaimsIdentity)principal.Identity;
var claimsIdentity = new ClaimsIdentity(
identity.Claims,
identity.AuthenticationType,
identity.NameClaimType,
identity.RoleClaimType);
...claims Web API call
return new ClaimsPrincipal(claimsIdentity);
}
}
This is the Web API client setup:
public class APIClient : IAPIClient
{
private readonly HttpClient _httpClient;
public APIClient(IHttpContextAccessor httpAccessor, HttpClient client, IConfiguration configuration)
{
var accessToken = httpAccessor.HttpContext.GetTokenAsync("access_token").Result;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.BaseAddress = new Uri(configuration["Api_Location"]);
_httpClient = client;
}
....
}
The problem arises because I don't understand DI that well. I want to use the APIClient that I created before it but, I am not sure how to pass that into the ClaimsLoader. I assume that it would be something like:
private readonly IHttpContextAccessor _httpAccessor;
private readonly IAPIClient _apiClient;
public ClaimsLoader(IHttpContextAccessor httpAccessor, IAPIClient apiClient)
{
_httpAccessor = httpAccessor;
_apiClient = apiClient;
}
But trying this cause the app to hang when starting. What am I missing? Ihave the API call create and working so that it will return a list of claims.
UPDATE 10/2/2019
I think I have found the issue is related to an infinite loop caused by the following line:
var accessToken = _httpAccessor.HttpContext.GetTokenAsync("access_token").Result;
This causes a call to AuthenticateAsync which in turn then calls this line again. Is there a way to get the Bearer token differently so as not to cause this loop?

How to Read Parameters sent to an Action Method (WebAPI) within a DelegatingHandler

I am using IHttpClientFactory for sending requests and receiving HTTP responses from an external APIs using Net Core 2.2.
I have implemented a DelegatingHandler to "intercept" my http request and add the Authorization header (Token). if token is not valid, It gets a new token and retry one more time.
Likewise, when I get a new token for the first time, I cache the token in-memory for further references. For caching the token I have created a dictionary that requires an accountID and the token.
The problem I have got is that the DelegatingHandler is registered in the Startup.cs class, but at that moment I do not have the accountID, I get the accountID as a parameter in the ActionMethod of the Controller. That action method is the one calling SendAsync and getting the token from the DelegatingHandler and so on.
I do not know, how I can inject that accountID into the DelegatingHandler after a request has been received in the controller.
I was trying creating a IClientCredentials interface and an implementation of that interface that can be instantiated in the controller and injected into the DelegatingHandler.
My Code looks like this:
The DelegatingHandler:
public class AuthenticationDelegatingHandler : DelegatingHandler
{
private readonly AccessTokenManager _accessTokenManager;
private readonly IClientCredentials _clientCredentials;
public AuthenticationDelegatingHandler(IHttpClientFactory httpClientFactory,
IOptions<AppSettings> appSettings, IClientCredentials clientCredentials)
{
_accessTokenManager = new AccessTokenManager(httpClientFactory, appSettings);
_clientCredentials = clientCredentials;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var clientCredentials = _clientCredentials.GetClientCredentials();
var accessToken = _accessTokenManager.GetToken(clientCredentials._accountID);
if (accessToken == null) {
accessToken = await _accessTokenManager.GetAccessTokenAsync(clientCredentials._accountID);
_accessTokenManager.AddOrUpdateToken(clientCredentials._accountID, accessToken);
}
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.access_token);
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
{
var token = await _accessTokenManager.GetAccessTokenAsync(clientCredentials._accountID);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.access_token);
response = await base.SendAsync(request, cancellationToken);
}
return response;
}
}
Startup.cs like that:
services.AddScoped<IClientCredentials>(_ => new
ClientCredentials("au","123"));
services.AddHttpClient("myClient")
.AddHttpMessageHandler<AuthenticationDelegatingHandler>();
And The controller:
[HttpPost("{siteName}/{accountID}")]
public async Task<ActionResult<AirRequest>> Post(AirModel model, string
siteName, string accountID)
{
....
SetClientCredentials(siteName, accountID);
var clientJAAPI =
_httpClientFactory.CreateClient("myClient");
var responseclientJAAPI = await
clientJAAPI.SendAsync(request);
.....
}
private ClientCredentials SetClientCredentials(string siteName, string
accountID) =>
new ClientCredentials(siteName, accountID);
You can use HttpContext.Items to pass the data.
(Not tested, sent from mobile).
In controller:
this.HttpContext.Items["accountId"] = accountId;
In your Handler inject IHttpContextAccessor
var accountId = _httpContextAccessor.HttpContext.Items["accountId"];
IHttpContextAccessor not registered by default, but can be registered by one of components you are using. If you get an exception, register it explicetly in DI:
services.AddHttpContextAccessor();
If IHttpContextAccessor type is missing add Microsoft.AspNetCore.Http nuget.
The data will sit there untill end of the request.

HttpContext Header

I have created this class for getting the Header value from requests.
public class AuthenticationHeader
{
private static IHttpContextAccessor _httpContextAccessor;
public AuthenticationHeader(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string AuthHeader => _httpContextAccessor.HttpContext?.Request.Headers["Authorization"];
}
and that I have registered that in my startup.cs like this
services.AddSingleton<AuthenticationHeader>();
And its been injected into my other classes like this.
public BaseClient(HttpClient client, ILogger<BaseClient> logger, AuthenticationHeader authHeader)
{
_client = client;
client.BaseAddress = new Uri("yrl");
client.DefaultRequestHeaders.Add("Accept", "application/json");
_logger = logger;
AuthHeader = authHeader;
}
Now as I have registered that as Singleton. So when call my Api for first time and provide the Authorization value in header the api is called successfully but the issue is when i pass empty Authorization header it still call's api successfully as it is storing old header value due to Singleton. How can I fix this? Is there any otherways to do what I am doing.
Try using HttpClientFactory, that was added Asp.Net Core 2.1, in conjunction with HttpMessageHandler to achieve what you are trying to do.
You can register the HttpClient in ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<BaseClient>(client =>
{
client.BaseAddress = new Uri("yrl");
client.DefaultRequestHeaders.Add("Accept", "application/json");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
}
With the above code in place, your BaseClient will receive the HttpClient instance via DI.
In order to validate/inspect the AuthHeader you can configure the HttpMessageHandler for the registered HttpClient. The code for the message handler is simple like below:
public class AuthHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("Authorization"))
{
return new HttpResponseMessage(HttpStatusCode.Forbidden)
{
Content = new StringContent("No Authorization header is present")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
In order to register the above handler, your code will look like below:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<AuthHeaderHandler>();
services.AddHttpClient<BaseClient>(client =>
{
//code omitted for brevity
...
})
.AddHttpMessageHandler<AuthHeaderHandler>();
}
You can inject whatever you need inside the message handler if needed. However, no need to inject the IHttpContextAccessor in the BaseClient. To read more about HttpClientFactory and HttpMessageHandlers please see this link and this. I hope this helps.
UPDATED ANSWER
Please have a look at the more concrete example of HttpMessageHandler that uses the IHttpContextAccessor and modifies the HttpRequestMessage i.e. adds the Authorization header before the call is made. You can modify the logic as per your need.
public class AuthHeaderHandler : DelegatingHandler
{
private readonly HttpContext _httpContext;
public AuthHeaderHandler(IHttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (_httpContext != null)
{
var accessToken = await _httpContext.GetTokenAsync(TokenKeys.Access);
if (!string.IsNullOrEmpty(accessToken))
{
// modify the request header with the new Authorization token
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}
}
return await base.SendAsync(request, cancellationToken);
}
}
UPDATED ANSWER 2
Please have a look at the simple solution that I have uploaded to GitHub. The solution is even simpler than I originally suggested. As you are not integrating any identity-based Authentication/Authorization, you can simply use a CustomActionFilter, I called it ValidateAuthHeader, to check if the AuthHeader is present or not and return the usual 403 if absent.
Within the ValidateAuthHeader, I have utilised the middleware code that you posted earlier. You can then simply add this attribute on the ActionMethods or Controllers which require this check.
Please have a look at the DataController and ValuesController. The DataController will receive the typed HttpClient that will be used to call the values endpoint. ValidateAuthHeader is present on the GetValues and will check for the AuthHeader. If it's absent it will generate the error.
[Route("api/[controller]")]
[ApiController]
public class DataController : ControllerBase
{
private readonly MyHttpClient _client;
public DataController(MyHttpClient client)
{
_client = client;
}
[ValidateAuthHeader]
public async Task<IActionResult> GetValues()
{
var response = await _client.GetAsync("api/values");
var contents = await response.Content.ReadAsStringAsync();
return new ContentResult
{
Content = contents,
ContentType = "application/json",
StatusCode = 200
};
}
}
The rest of the flow is the same as I originally suggested. The call will be passed through the AuthHeaderHandler which is an HttpMessageHandler for the registered MyHttpClient. Please have a look at the Startup.cs.
The handler will retrieve the HttpContext via HttpContextAccessor and will check for the AuthHeader. If present, it will add it to the RequestMessage parameter.
I hope this helps. Feel free to ask any questions that you may have.
Setting Auth Header without using HttpMessageHandler
Modify the MyHttpClient and add a public method called SetAuthHeader
public class MyHttpClient
{
private readonly HttpClient _httpClient;
public MyHttpClient(HttpClient client)
{
_httpClient = client;
}
public void SetAuthHeader(string value)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", value);
}
}
Then call this method in your action method as you will have the AuthHeader in the HttpContext.Request at that point
[ValidateAuthHeader]
public async Task<IActionResult> GetValues()
{
var authHeader = Request.Headers["Authorization"];
_client.SetAuthHeader(authHeader.First());
var response = await _client.GetAsync("api/values");
var contents = await response.Content.ReadAsStringAsync();
return new ContentResult
{
Content = contents,
ContentType = "application/json",
StatusCode = 200
};
}
Remove the AuthHeaderHandler registration and delete the AuthHeaderHandler.

How do we log authentication token requests in ASP.NET Web API

We have an ASP.NET Web Api application which uses OAuth Bearer Tokens for authentication, for which we are trying to implement Request/Response logging.
Basically it works like this:
1. User sends request to "/authenticate" and receives an authentication token
2. User then uses this authentication token for requests to the exposed API methods
For logging requests to the exposed API methods, we use a DelegatingHandler which works perfectly fine.
However, requests made to "/authenticate" are not captured by the DelegatingHandler implementation.
Is there a different approach required for logging requests for tokens?
public abstract class MessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var correlationId = Guid.NewGuid();
var requestInfo = string.Format("{0} {1}", request.Method, request.RequestUri);
var requestContent = await request.Content.ReadAsByteArrayAsync();
var context = ((HttpContextBase)request.Properties["MS_HttpContext"]);
await IncomingMessageAsync(correlationId, request.Method, request.RequestUri, request.Headers, requestContent,
context.Request.UserHostAddress, context.Request.IsAuthenticated, context.User.Identity.Name);
var response = await base.SendAsync(request, cancellationToken);
byte[] responseMessage;
responseMessage = await response.Content.ReadAsByteArrayAsync();
await OutgoingMessageAsync(correlationId, response.StatusCode, response.Headers, responseMessage);
return response;
}
protected abstract Task IncomingMessageAsync(Guid correlationId, HttpMethod requestMethod, Uri requestUri, HttpRequestHeaders requestHeaders, byte[] messageContent, string ipAddress, bool isAuthenticated, string requestMadeByUserName);
protected abstract Task OutgoingMessageAsync(Guid correlationId, HttpStatusCode statusCode, HttpResponseHeaders responseHeaders, byte[] messageContent);
}
EDIT w/ OAuth Code
[assembly: OwinStartup(typeof(MyApp.Infrastructure.IdentityConfig))]
namespace MyApp.Infrastructure
{
public class IdentityConfig
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext<ApplicationIdentityDbContext>(() => ApplicationIdentityDbContext.Create(ConfigurationDataProvider.MYDBCONNSTRING));
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
{
Provider = new ApplicationAuthProvider(),
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Authenticate")
});
}
}
}
You are installing OWIN middleware to issue tokens before the WebAPI middleware.
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
{
Provider = new ApplicationAuthProvider(),
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Authenticate")
});
The DelegatingHandler you try to use to log the request is part of the Web API middeware and is never reached because the token issuing middleware handles the request and does not call middleware further in the pipeline.
Instead of using DelegatingHandler, use the following middleware and install it before the token middleware.
public class RequestLoggerMiddleware
{
private readonly Func<IDictionary<string, object>, Task> _next;
private readonly ILogger _logger;
public RequestLoggerMiddleware(
Func<IDictionary<string, object>, Task> next,
ILogger logger)
{
_next = next;
_logger = logger;
}
public Task Invoke(IDictionary<string, object> environment)
{
var context = new OwinContext(environment);
_logger.Write($"{context.Request.Method} {context.Request.Uri.AbsoluteUri}");
var result = _next.Invoke(environment);
_logger.Write($"Status code: {context.Response.StatusCode}");
return result;
}
}
To install the middleware, just insert the statement: app.Use(typeof (RequestLoggerMiddleware)); before the app.UseOAuthBearerTokens statement in your Startup.cs.

Categories

Resources