How to Read Parameters sent to an Action Method (W

2019-07-25 08:58发布

问题:

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);

回答1:

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.