How to invoke a NSwag client method that needs bearer token on request header?
Asked Answered
I

4

30

I didn't get exactly how NSwag interact with IdentityServerX bearer tokens and adds it request header conventionally? My host api application implements IdentityServer3 with LDAP auth, so as far as i understand; if any host needs to a token for authentication then any client must send it on request header. So how can i deal with it while working NSwag clients ?

Any idea appreciated. Thanks.

Ingredient answered 13/10, 2016 at 15:57 Comment(0)
I
25

I've resolved the issue by partial method. My example is:

CampaignClient.cs

public partial class CampaignClient
{
    partial void PrepareRequest(HttpClient request, ref string url);

    partial void ProcessResponse(HttpClient request, HttpResponseMessage response);

    //some codes...
}

CampaignClient.Extensions.cs - partial class:

public partial class CampaignClient
{
    private readonly IRequestContext _requestContext;
    private readonly IStartupConfiguration _startupConfiguration;

    public CampaignClient(IRequestContext requestContext, IStartupConfiguration startupConfiguration)
    {
        _requestContext = requestContext;
        _startupConfiguration = startupConfiguration;
    }

    partial void PrepareRequest(HttpClient request, ref string url)
    {
        request.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _requestContext.GetBearerTokenOrTriggerUnauthException());
    }
}

Method override has saved me!

Ingredient answered 21/10, 2016 at 14:13 Comment(2)
This way will become useless if you have more than a dozen classes in your API, You should use the base class to handle your httpClient object used in the request. Additionally, you should take into consideration the automated token prolongation on the first recipient of 401 status. I will put a detailed answer the soon possible timePulsatory
Curious what is GetBearerTokenOrTriggerUnauthException?Auxiliaries
L
30

@oguzhan-soykan and @peter answers are both good - Here's an expansion of @peter's answer to show how you can implement a base class and not repeat yourself for every API client.

Requirements

  • NSwag.MSBuild package
  • Swagger .JSON definition

Create a base 'Client' class that exposes the functionality you need. Likely a bearer token property.

public abstract class MySwaggerClientBase
{
    public string BearerToken { get; private set; }

    public void SetBearerToken(string token)
    {
        BearerToken = token;
    }

    // Called by implementing swagger client classes
    protected Task<HttpRequestMessage> CreateHttpRequestMessageAsync(CancellationToken cancellationToken)
    {
        var msg = new HttpRequestMessage();
        // SET THE BEARER AUTH TOKEN
        msg.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", BearerToken);
        return Task.FromResult(msg);
    }

}

Edit your swagger code generation command to make use of the base class for all generated clients and use the UseHttpRequestMessageCreationMethod option.

<Project>
 ...
<Exec Command="$(NSwagExe) swagger2csclient /input:path-to-swagger-definition.json /output:$(ProjectDir)\Swagger.generated.cs /Namespace:MyNameSpace /ClientBaseClass:MySwaggerClientBase /UseHttpRequestMessageCreationMethod:true" />
 ...
</Project>
Leg answered 12/4, 2018 at 16:28 Comment(3)
It might be useful to show where/how to call the SetBearerToken method. Since it's not a static method, it can't just be called anywhere, and since MySwaggerClientBase is abstract you can't just instantiate a new object of that type.Pamella
@GeraldMurphy my answer shows now this abstract class becomes the base class for all message types. See the last portion where the csproj is modified.Leg
@AaronHudon its unclear how to instantiate the generated client and provide both the baseUrl and httpClient together with a bearer token, these are different constructors.Rolfrolfe
I
25

I've resolved the issue by partial method. My example is:

CampaignClient.cs

public partial class CampaignClient
{
    partial void PrepareRequest(HttpClient request, ref string url);

    partial void ProcessResponse(HttpClient request, HttpResponseMessage response);

    //some codes...
}

CampaignClient.Extensions.cs - partial class:

public partial class CampaignClient
{
    private readonly IRequestContext _requestContext;
    private readonly IStartupConfiguration _startupConfiguration;

    public CampaignClient(IRequestContext requestContext, IStartupConfiguration startupConfiguration)
    {
        _requestContext = requestContext;
        _startupConfiguration = startupConfiguration;
    }

    partial void PrepareRequest(HttpClient request, ref string url)
    {
        request.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _requestContext.GetBearerTokenOrTriggerUnauthException());
    }
}

Method override has saved me!

Ingredient answered 21/10, 2016 at 14:13 Comment(2)
This way will become useless if you have more than a dozen classes in your API, You should use the base class to handle your httpClient object used in the request. Additionally, you should take into consideration the automated token prolongation on the first recipient of 401 status. I will put a detailed answer the soon possible timePulsatory
Curious what is GetBearerTokenOrTriggerUnauthException?Auxiliaries
C
9

For the c# client you can specifcy UseHttpClientCreationMethodor UseHttpRequestMessageCreationMethod

(Cf. https://github.com/RicoSuter/NSwag/blob/master/src/NSwag.CodeGeneration.CSharp/SwaggerToCSharpClientGeneratorSettings.cs)

That way NSwag expects you to implement the methods for Creating A HttpClient or HttpRequest. You can set your headers there without any magic

Click answered 21/10, 2016 at 14:42 Comment(3)
how can you use this for the SwaggerUi ?Apparitor
this is an option for the swagger client generator. the swagger ui is a different thing as far as I know. Could be that the swagger ui is generated using a client generator, but I'm not sure. Maybe make this a questionClick
#46236652 @ClickApparitor
T
4

If you want to use interfaces instead of abstract classes, then this is the solution for that case

  1. Set the option to generate the interface for the Client

    <OpenApiReference Include="OpenAPIs\swagger.json" CodeGenerator="NSwagCSharp" Namespace="YourNameSpace" ClassName="YourClient" >
        <Options>/GenerateClientInterfaces:true</Options>
    </OpenApiReference>
    
  2. Add a second part to the partial interface that is generated by NSwag

    public partial interface IYourClient
    {
        void SetBearerToken(string token);
    }
    
  3. Add a second part to the partial client that is generated by NSwag

    public partial class YourClient
    {
        private string? _token;
    
        public void SetBearerToken(string token) =>
            _token = token;
    
        partial void PrepareRequest(HttpClient client, HttpRequestMessage request, string URL)
        {
           if (!string.IsNullOrEmpty(_token))
              request.Headers.Authorization = new("Bearer", _token);   
        }
    }
    

The benefit of using the interface over abstract classes is all the operations will be defined in the interface, but the base class does not create abstract methods in the base class

Toothsome answered 1/7, 2023 at 15:47 Comment(1)
as @AbuDawoodOussama already mentioned - its not the best option if you have a dozens on httpClientsDao

© 2022 - 2024 — McMap. All rights reserved.