简体   繁体   中英

Best way to call different APIs based on user input in C#?

I am developing one console application using C#. I need to call different-2 get APIs based on the input provided by the user and write the response on console.

So if user enters 1, I need to call a group weather api. If user enters 2 then I need to call temperature API. If user enters 3 then call some other API and if enters other then this just write invalid input.

For now I have just written multiple if and else if . Is there any better approach? Because this way there are multiple if else. And same goes for switch case as well.

I have also thought of creating mapping of input the endpoint like defining them in config file and then read into a dictionary.

So in future it can support below things.

if I need to call another API for input 4….. or No need to call any API for input 2. Or call different API on input 2.

well one way around this is to use Dictionary and Action :

private static readonly Dictionary<string, Action> MyApiCalls= new()
    {
        {
            "1", () =>
            {
                Console.WriteLine("im cool");
            }
        },

        {
            "2", () =>
            {
                Console.WriteLine("im really cool");
            }
        }
    };

and you use it like this :

 static void Main(string[] args)
    {
        var input = Console.ReadLine() ?? "1";

        MyApiCalls[input]();

        Console.ReadKey();
    }

so the action can be your api call or anything else.

if the number of cases is limited, I suggest to stick to a switch (cleaner then if-else in my opinion). A switch doesn't cost anything in speed.

Happy programming!

You can use Strategy pattern . In addition, it is necessary to use Factory method pattern to resolve API instances.

You can choose any strategy at the runtime based on your parameter.

Let me show an example of implementation. So we need some user types:

public enum UserInput
{
    One, Two, Three
}

And some abstractions that can be implemented by API strategies:

public abstract class BaseApi
{
    public abstract string Get();
}

And its concrete implementations:

public class WeatherApi : BaseApi
{
    public override string Get() => "I am weather API";
}

public class TemperatureApi : BaseApi
{
    public override string Get() => "I am temperature Api";
}

And it is necessary to implement Factory pattern to generate API strategies by UserInput :

public class ApiFactory
{
    private Dictionary<UserInput, BaseApi> _apiByUserInput { get; set; }
        = new Dictionary<UserInput, BaseApi>
        {
            { UserInput.One, new WeatherApi() },
            { UserInput.Two, new TemperatureApi() }
        };

    public BaseApi GetInstance(UserInput userInput) => _apiByUserInput[userInput];
}

And this is a method which works with ApiFactory:

string CallApiByUserInput(UserInput userInput) 
{
    ApiFactory apiFactory = new ApiFactory();
    BaseApi weatherApi = apiFactory.GetInstance(userInput);
    return weatherApi.Get();
}

And then code can be called like this:

Console.WriteLine(CallApiByUserInput(UserInput.One)); // OUTPUT: "I am weather API"

UPDATE

If you want to have a single instance of your API's, then let me show some ways.

The first way

If you are using IoC container, then you can set object lifescope.

Let me show an example for ASP.NET Core.

Register all in the DI in your Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    // ... other code is omitted for the brevity
    services.AddSingleton<WeatherApi>();
    services.AddScoped<TemperatureApi>(); // this can be Singleton 
        // or Transient as well, depending on your needs
    // ... other code is omitted for the brevity
}

and inject your dependencies through constructor of ApiFactory :

public class ApiFactory
{
    private Dictionary<UserInput, BaseApi> _apiByUserInput;

    public ApiFactory(WeatherApi weatherApi, TemperatureApi temperatureApi)
    {
        _apiByUserInput = new Dictionary<UserInput, BaseApi>()
        {
            { UserInput.One, weatherApi },
            { UserInput.Two, temperatureApi }
        };
    }

    public BaseApi GetInstance(UserInput userInput) => _apiByUserInput[userInput];
}

The second way

Make ApiFactory to be static :

public class ApiFactory
{
    private static Dictionary<UserInput, BaseApi> _apiByUserInput { get; set; }
        = new Dictionary<UserInput, BaseApi>
        {
            { UserInput.One, new WeatherApi() },
            { UserInput.Two, new TemperatureApi() }
        };

    public BaseApi GetInstance(UserInput userInput) => _apiByUserInput[userInput];
}

and it can be called like this:

Console.WriteLine(CallApiByUserInput(UserInput.One)); // I am weather API 2022-07-21 11:01
Thread.Sleep(3000);
Console.WriteLine(CallApiByUserInput(UserInput.Two)); // I am temperature API 2022-07-21 11:01

I'm sceptical of

I have also thought of creating mapping of input the endpoint like defining them in config file and then read into a dictionary.

because different API will have different return object and you need custom code to extract the relevant data.

I think switch has a good balance of simplicity and flexibility:

await answer switch 
{
        "1" => CallWeatherApiAsync(),
        "2" => CallTemperatureApiAsync(),
        _ => throw / continue (if in a loop)
}

You could also use a collection Action / Func<Task> :

Dictionary<string, (string Description, Func<Task> Work)> functionality; 
List<(string Key, string Description, Func<Task> Work)> functionality;

Please remember that you may need to balance generality and specific behaviours. For example, you may require extra input for certain functions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM