Skip to content

digipolisantwerp/serviceagents_aspnetcore_deprecated

Repository files navigation

ServiceAgents Toolbox

Toolbox for ServiceAgents in ASP.NET Core.

This readme is applicable for toolbox version 7.0.x

Table of Contents

Installation

To add the toolbox to a project, you add the package to the csproj project file:

  <ItemGroup>
    <PackageReference Include="Digipolis.ServiceAgents" Version="7.1.0" />
  </ItemGroup>

or if your project still works with project.json :

    "dependencies": {
        "Digipolis.ServiceAgents":  "7.1.0"
    }

ALWAYS check the latest version here before adding the above line !

In Visual Studio you can also use the NuGet Package Manager to do this.

Usage

To add a single service agent in your project simply use the AddSingleServiceAgent<T> extension in the ConfigureServices method in the Startup class. The type represents the implementation of your service agent. It will be registered as a typed client, i.e. transient service which expects a HttpClient to be injected in via the constructor. If the service agent implements an interface of the same name preceded with the letter ‘I’, the interface will be registered with the Service Agent as implementation type. Generic types must be registered via AddSingleServiceAgent.

    services.AddSingleServiceAgent<YourServiceAgent>(settings =>
    {
        settings.Scheme = "http";
        settings.Host = "test.com";
        settings.Path = "api";
        settings.Headers = new Dictionary<string, string>()
        {
            { "apikey", "apikeyvalue" },
            { "X-Custom-Header", "customheadervalue" },
        };
    });

The parameter of type Action<ServiceSettings> allows you to set the settings for the service agent.

If you want to configure the agent using a configuration file or if you want to register multiple service agents, use the AddServiceAgents extension instead.

    services.AddServiceAgents(settings =>
    {
        settings.FileName = Path.Combine(ConfigPath, "serviceagents.json");
    });

If your agents are located in a different assembly then the executing assembly, you can pass that assembly as a parameter, this is optional.

    services.AddServiceAgents(settings =>
    {
        settings.FileName = Path.Combine(ConfigPath, "serviceagents.json");
    },
	assembly: AssemblyWithAgents);

The json file contains one or multiple sections, one per service agent.

The structure of the json file is:

{
    "Global": {
    },
    "TestAgent": {
      "AuthScheme": "None",
      "Host": "test.be",
      "Path": "api",
      "Port": "5001",
      "Scheme": "http",
      "Headers": {
          "apikey": "123456789",
          "X-Custom-Header": "customheadervalue"
      }
    }
}

A first object (section) section named Global can optionally be defined. THis section is intended for settings that are common to all the service agents.

Each other object (section) in the json represents a service agent type. The object name has to match the service agent type name. See the Creating a service agent section for more info on creating the service agents.

Following options can be set per section (service agent):

Option Description Default Mandatory
AuthScheme The authentication scheme to be used by the service agent. "None"
Host The host part of the url for the service agent. "" X
Path The path part of the url for the service agent. ""
Port The port for the service agent. 443 fo https, 80 for http
Scheme The scheme of the url for the service agent. "https"
Headers A key-value collection representing the headers to be added to the requests. null
BasicAuthUserName The user name used for basic authentication scheme. "" With BasicAuth scheme
BasicAuthPassword The password used for basic authentication scheme. "" With BasicAuth scheme
OAuthPathAddition Oauth path addition for OAuth authentication scheme. "" With Oauth scheme
OAuthClientId Oauth client id for OAuth authentication scheme. "" With Oauth scheme
OAuthClientSecret Oauth client secret for OAuth authentication scheme. "" With Oauth scheme
OAuthScope Oauth scopt for OAuth authentication scheme. "" With Oauth scheme

All the url parts form the basic url for the service agent: {scheme}://{host}:{port}/{path}/

An overloaded method for both AddSingleServiceAgent<T> and AddServiceAgents is available where you can pass a "clientCreatedAction" parameter of type Action<IServiceProvider, HttpClient> that gets invoked when the underlying HttpClient gets created. That way you can customize the created client.

Important notice: the action gets invoked for every service agent when multiple are registered!

    services.AddServiceAgents(s =>
    {
        s.FileName = Path.Combine(ConfigPath, "serviceagents.json");
        s.Section = "TestAgent";
    }, ...,
    (serviceProvider, client) =>
    {
        //customize the client
    });

With this overloaded method of AddSingleServiceAgent<T> or AddServiceAgents it is also possible to pass a "clientBuiltAction" parameter of type Action<string, IHttpClientBuilder>. This gets invoked after the registration of the Service Agent as a typed client and allows you to add custom behaviour with the provided HttpClientBuilder, ex. adding delegating handers. The String argument is set to the Service Agent's type name.

Important notice: the action gets invoked for every service agent when multiple are registered!

    services.AddServiceAgents(s =>
    {
        s.FileName = Path.Combine(ConfigPath, "serviceagents.json");
        s.Section = "TestAgent";
    }, 
	...,
    (string, httpClientBuilder) =>
    {
        //customize the client
        httpClientBuilder.AddHttpMessageHandler<ValidateHeaderHandler>();
    });

Creating a service agent

In order to create a service agent you need to create a type that derives from AgentBase.

    public class DemoAgent : AgentBase
    {
        public DemoAgent(HttpClient client, IServiceProvider serviceProvider, IOptions<ServiceAgentSettings> options)
            : base(client, serviceProvider, options)
        {
        }
    }

The AgentBase class contains several protected methods to perform the basic http actions (get, post, put, patch and delete). All the methods are async.

Some examples:

Implement a get operation

    public class DemoAgent : AgentBase
    {
        public DemoAgent(HttpClient client, IServiceProvider serviceProvider, IOptions<ServiceAgentSettings> options)
            : base(client, serviceProvider, options)
        {
        }

        //A basic get operation
        public Task<Address> GetAddressAsync(int id)
        {
            return base.GetAsync<Address>($"adress?id={id}");
        }

        //A basic post operation
        public Task<Address> PostAddressAsync(Address adress)
        {
            return base.PostAsync<Address>("adress", adress);
        }
    }

If you want to return the response as string you can use the GetStringAsync method on the Agentbase.

    public Task<string> GetAddressAsStringAsync(int id)
    {
        return base.GetStringAsync($"adress?id={id}");
    }

If you want to get the response itself, you can use the GetResponseAsync method on the Agentbase. Only responses with status success will be returned.

    public Task<HttpResponseMessage> GetServiceMethodResponseAsync(int id)
    {
        return base.GetResponseAsync($"adress?id={id}");
    }

Using a service agent

In order to use a service agent simply ask the serviceProvider for an instance of the agent's type and use it.

For example in a controller:

    public class ValuesController : Controller
    {
        private DemoAgent _serviceAgent;

        public ValuesController(DemoAgent serviceAgent)
        {
            _serviceAgent = serviceAgent;
        }

        [HttpGet]
        public async Task<string> Get()
        {
            var result = await _serviceAgent.GetAddressAsync(1);

            //do something...

            return "somestring";
        }
    }

If your agent implements an interface with the same name preceded with an I, the agent is registered as an implementation of that interface. In that case you need to request an instance of the interface type:

    //The service agent implementation
    public class DemoAgent : AgentBase, IDemoAgent
    {
        public DemoAgent(HttpClient client, IServiceProvider serviceProvider, IOptions<ServiceAgentSettings> options)
            : base(client, serviceProvider, options)
        {
        }

        //Some implementation
    }

    //The usage
    public class ValuesController : Controller
    {
        private IDemoAgent _serviceAgent;

        public ValuesController(IDemoAgent serviceAgent)
        {
            _serviceAgent = serviceAgent;
        }
    }

Authentication schemes

Different schemes are available to be used with the service agents.

Possible schemes:

  • None
  • Bearer
  • OAuthClientCredentials
  • Basic

Note! Since version 4.0.0 of the toolbox the "ApiKey" scheme has been removed. If you need to add api key to the request, use the "Headers" property of the ServiceSettings. This allows you to combine an authentication scheme with an api key header.

Check the Creating a service agent section to see how to define the scheme for your service agents.

None

No authentication is done.

Bearer

With the Bearer scheme the authentication is done through use of the authorization header:

Authorization Bearer xxx

where xxx is the token. To use the Bearer scheme set the AuthScheme property of the ServiceSettings object to the value "Bearer". The token is extracted from the UserToken property of the AuthContext object provided by the dependency injection infrastructure.

OAuthClientCredentials

This will use the OAuth2 client credentials flow to obtain a (bearer)token from a token endpoint.

To use the OAuth Clientcredentials scheme set the AuthScheme property of the ServiceSettings object to the value "OAuthClientCredentials".

You must also supply following settings in the ServiceSettings:

  OAuthClientId = "f44d3641-8249-440d-a6e5-61b7b4893184";
  OAuthClientSecret = "2659485f-f0be-4526-bb7a-0541365351f5";
  OAuthScope = "testoauthDigipolis.v2.all";
  OAuthPathAddition = "oauth2/token";

Before each call a check occurs to validate the lifetime of the OAuth access token. If no valid token is found, a new one is requested with the provided configuration settings.

See the SampleApi for more info.

Basic

With the Basic scheme the authentication is done through use of a Basic Authentication header.

Authorization Basic yyy

where yyy is the base 64 encoded username and password, with username containing an optional domain

Basic authentication can only be used with an "https" scheme except for the Development environment!

To use the Basic scheme set the AuthScheme property of the ServiceSettings object to the value "Basic". The user name and password can be entered in the settings.

{
    "Global": {
    },
    "TestAgent": {
      "AuthScheme": "Basic",
      "Host": "test.be",
      "Path": "api",
      "Port": "5001",
      "Scheme": "https",
      "BasicAuthDomain": "domain",
      "BasicAuthUserName": "userName",
      "BasicAuthPassword": "password"
    }
}

If you don't want to enter your user name and password in the json configuration file you can use an overload of the AddServiceAgents method that accepts a parameter of type Action<ServiceAgentSettings> to alter the values of the service settings after they have been loaded from the json file.

    services.AddServiceAgents(json =>
    {
        json.FileName = Path.Combine(ConfigPath, "serviceagents.json");
    }, serviceAgentSettings =>
    {
        var settings = serviceAgentSettings.Services.Single(s => s.Key == nameof(TestAgent)).Value; 
		settings.BasicAuthDomain = "domainfromcode"
        settings.BasicAuthPassword = "userNamefromcode";
        settings.BasicAuthUserName = "passwordfromcode";
    }, null);