Using Other Verbs with Web API

Full source code available here.

You will often use the GET verb when making requests to an API. You have probably used it like this –

www.example.com/person/ or
www.example.com/person/1 or
www.example.com/person?firstname=dave&lastname=daniels&age=22

All very simple, but what if you wanted to to search for many people at the same time, and I don’t mean all the people aged 22, or all the people with the first name Dave.

What if you wanted to look for Dave, Tom, Steve and Dan in one request, or all the people aged 22, 33, or 44.

You could go down the road of adding them to the query string, something like

www.example.com/person?firstperson_firstname=dave&secondperson_firstname=tom&...

But this a bad idea.

This is where the HTTP SEARCH method comes in. If this doesn’t sound familiar, that might be because it is currently an IETF draft https://tools.ietf.org/html/draft-snell-search-method-00, but tools like Fiddler support it.

SEARCH differs from GET by having a body. In this body you can have any content you want. For our scenario we will use a json body with an array of things to search for. You have to specify in the header what the content type of the body, e.g. Content-Type: application/json, (just like you do with a POST or a PUT).

I’ve created a very simple POCO to represent the things I want to search by.

public class PersonSearchModel
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public int Age { get; set; }
}

Into the body of the SEARCH request I have a json array of PersonSearchModel.

[
  {
    "firstname": "Dave",
    "lastname": "Davis",
    "age": 22
  },
  {
    "firstname": "Larry",
    "lastname": "Landers"
  },
  {
    "firstname": "Steve",
    "lastname": "Smith",
    "age": 44
  }
]

Now we can search for as many people as we like in as complicated way as we like.

In the PersonController I have a Search method –

[AcceptVerbs("SEARCH")]
public async Task<IActionResult> Search([FromBody]List<PersonSearchModel> people)
{
	// Perform search
	// snip..
}

Inside the body of the method you can search in any manner you choose.
here.

Full source code available here.

Loading Config from Multiple Sources with .NET Core 2.x Web Api or MVC

Full source code available here.

.NET Core 2 and .NET Core 2.1 offer many ways to load configuration and they are well documented by Microsoft. But there is one scenario that I didn’t see explained.

If you want to supplement the configuration in appsettings.json with more from a remote service, database or some other source, you first need to know where that source is, then make a request to it and add it to your configuration. You are probably going to put the location of the remote configuration appsettings.json, then you call the remote config source.
Sounds easy? It is, but not obvious.

First, a little background.

In an out of the box Web API or MVC application you get a Program.cs that looks like this –

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

On line 9, we have WebHost.CreateDefaultBuilder(args), if you peek the definition of this with Resharper you will some something like –

The highlighted code is how your appsettings.json is loaded into the configuration and added to services collection, this call happens just as you leave Program.cs

But you want to read from the appsettings.json and use a value from it to make another call to get more configuration and then add the whole lot of the services collection. You might also want to setup some logging configuration in Program.cs, so you need access to all configuration settings before calling

CreateWebHostBuilder(args).Build().Run();

Here’s how you do it.

Step 1
Inside the main method in Program.cs, build the configuration yourself –

IConfigurationBuilder builder = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json");
Configuration = builder.Build();

Get the location of the other configuration service, in my example it is another json file.
Add it to the builder and call build.

string otherConfigService = Configuration["otherConfigService"]; // this could be a database or something like consul

builder.AddJsonFile(otherConfigService);
Configuration = builder.Build();

Now you have access to the configuration values from the second source.

Configuration["SomeOtherConfigItem1"]}
Configuration["SomeOtherConfigItem2"]}

So far so good, but as mentioned above, the CreateDefaultBuilder adds the configuration to the ServiceCollection, making it available by DI. But that only happens for appsettings.json (and appsettings.{env.EnvironmentName}.json, not the config you loaded from the other source.

If you tried to access the a config setting of your from inside Startup.cs or a controller, it would not be there.

Step 2
Let’s make our configuration available via the ServicesCollection.

In the first block of code above there was a call – CreateWebHostBuilder(args).Build().Run();

I added line 4 below, this will add the configuration to the services collection.

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseConfiguration(Configuration); // add this line to add your configuration to the service collection

Now, the config values loaded from appsettings.json and your secondary config source will be available throughout your application.

Full source code available here.