What I need to do here is to create an API that can authenticate and ingest an XML POST request, saving the provided data into a SQL database and returning a relevant HTML status code. Unfortunately this wasn’t quite as easy as I’d anticipated as the XML data will be coming in with a variety of namespaces and the authentication header needs to be checked before deserialization of the XML takes place. After a lot of fruitless Googling I discovered middleware within ASP.NET Core and the second half of this guide is based on what I learnt here.

First off, create a new ASP.NET Core Web Application project. One thing to note is that the name of the project shouldn’t include any spaces or you’ll get the following error when you try to build it.

Description: Source file {src path}.deps.json could not be found

Where {src path} is the local path to your project, the end of which will be the final word from your project name (missing any preceding words).

Create it using the Web API template.

By default Web API only works with JSON, in order to enable it to work with XML we need to add in the NuGet package Microsoft.AspNetCore.Mvc.Formatters.Xml.

We then need to update ConfigureServices in Startup.cs with the following code.

public void ConfigureServices(IServiceCollection services)
{
	// Add framework services.
	services.AddMvc(config => 
	{
	// Add XML Content Negotiation
	config.RespectBrowserAcceptHeader = true;
	config.InputFormatters.Add(new XmlSerializerInputFormatter());
	config.OutputFormatters.Add(new XmlSerializerOutputFormatter());
	});
}

The XML I need to ingest is provided everytime the stock level of an item changes in a store and looks like the below.

<?xml version="1.0"?>
<stk:Stock xmlns:stk="http://schemas.storegroup.com/stock">
	<stk:LastUpdated>2015-09-07T13:00:00+01:00</stk:LastUpdated>
	<stk:LocationList>
		<loc:Location xmlns:loc="http://schemas.storegroup.com/location"
		              id="9043"/>
	</stk:LocationList>
	<bsk2:Basket xmlns:bsk2="http://schemas.storegroup.com/basket-v2">
		<bsk2:ItemList>
			<cmn:Item id="9371840"
			          xmlns:cmn="http://schemas.storegroup.com/common">
				<cmn:Quantity type="availablequantity">1</cmn:Quantity>
			</cmn:Item>
		</bsk2:ItemList>
	</bsk2:Basket>
</stk:Stock>

First off I need to create a model that matches this structure that it can be deserialized to. It turns out this is actually as easy as just coppy pasting the XML (facpalm) but the middleware layer is still needed for authorization so all is not yet lost. If you copy the XML and then in a script (outside a class) select Edit > Paste Special > Paste XML As Classes then a class will be created for you withou scoping it yourself. This is only available in Visual Studio 2015 but it seems not to work in ASP.NET Core projects with an error that it “failed due to System.ArgumentException: Unable to find a type of reference that is appropriate for this file: System”. It works in standard .NET projects so if you need it then it’s worth creating one just for creating the class and then nuking it afterwards.

Middleware

Create a new folder in the project called “Middleware” and then add a class to it called “AuthorizationMiddleware.cs”, this is the middleware that will look for the authorization header and check to see if it is valid or not.

private readonly RequestDelegate _next;

public AuthorizationMiddleware(RequestDelegate next)
{
	_next = next;
}

public async Task Invoke(HttpContext context)
{
	// Need to convert to uppercase as Advanced Rest Client seems to lowercase all custom keys
	// Only really a problem for testing rather than live
	foreach (string key in context.Request.Headers.Keys)
	{
		string keyUpper = key.ToUpper();
		
		if (keyUpper == "API-PASSKEY")
		{
			string passKey = context.Request.Headers[key];
			if (!CheckAuthorized(passKey))
			{
				context.Response.StatusCode = 401; //Unauthorized
				return;
			}
		}
		else
		{
			context.Response.StatusCode = 401; //Unauthorized
			return;
		}
	}
	
	await _next.Invoke(context);
}

You then need to have the “CheckAuthorization” function to check the provided header value, in my case this is against a key list stored in a SQL database hosted in Azure.


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *