Skip to content

Instantly share code, notes, and snippets.

@Zifah
Last active March 31, 2018 22:36
Show Gist options
  • Save Zifah/59178032bdc4b7869b7cdea1ba25c5af to your computer and use it in GitHub Desktop.
Save Zifah/59178032bdc4b7869b7cdea1ba25c5af to your computer and use it in GitHub Desktop.
A .NET MVC filter attribute which can parse parameters out of a USSD short-code using regular expression
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Text.RegularExpressions;
namespace Ussd.Application.Filters
{
public class ExtractParamsAttribute : ActionFilterAttribute
{
private readonly string _shortCodePattern;
public ExtractParamsAttribute(string shortCodePattern)
{
// Set the expected short-code pattern while instantiating the filter attribute
_shortCodePattern = shortCodePattern;
}
/// <summary>
/// <para>
/// You can pre-process the HTTP request here before handing over control to the controller action
/// </para>
/// <para>
/// In this case, we pass the request to the method: Process() to do the pre-processing
/// </para>
/// </summary>
/// <param name="actionContext">
/// This object contains the request payload which we want to modify during pre-processing
/// </param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
Process(actionContext);
base.OnActionExecuting(actionContext);
}
/// <summary>
/// The 'asynchronous' version of OnActionExecuting; same documentation applies
/// </summary>
/// <param name="actionContext"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public override Task OnActionExecutingAsync(HttpActionContext actionContext,
CancellationToken cancellationToken)
{
Process(actionContext);
return base.OnActionExecutingAsync(actionContext, cancellationToken);
}
/// <summary>
/// Extract the payload from the request, then pass it to the method
/// that will populate it with extracted parameters (PopulateParamsFromShortCode)
/// </summary>
/// <param name="actionContext">
/// This object contains the request payload which we want to modify during pre-processing
/// </param>
private void Process(HttpActionContext actionContext)
{
var payload = new UssdPayload();
if (actionContext.ActionArguments.ContainsKey("payload"))
{
payload = actionContext.ActionArguments["payload"] as UssdPayload;
}
PopulateParamsFromShortCode(payload);
}
/// <summary>
/// Populate the request payload with the parameters in the short-code, if any
/// </summary>
/// <param name="payload">The request payload</param>
public void PopulateParamsFromShortCode(UssdPayload payload)
{
// assign the short code to a local variable for simpler referencing
string shortCode = payload.serviceCode;
// END execution; nothing to extract
if (string.IsNullOrWhiteSpace(shortCode))
{
return;
}
/* Check if the short-code matches the pattern used to
* instantiate this instance of the ExtractParams attribute */
var match = Regex.Match(shortCode, _shortCodePattern);
// END execution; short-code does not match regex pattern
if (!match.Success)
{
return;
}
var theMatch = match.Value;
/* split the short-code pattern into two parts:
* the ignored part that serves as format descriptor,
* and the actual pattern used for matching */
var sections = _shortCodePattern.Split(new[] { "(?!", "#)" },
StringSplitOptions.RemoveEmptyEntries);
// END execution; I expect two items in the sections array (see previous comment)
if (sections.Count() != 2)
{
return;
}
/* the first member of the array is the format descriptor
* e.g. TRANSFER_TYPE*AMOUNT*ACCOUNT_NUMBER# */
var format = sections[0];
// remove the trailing # in the matching part of the short-code
theMatch = theMatch.Replace("#", string.Empty);
//split the format descriptor to get a list of the short-code parameter names
var formatSplit = format.Split('*');
/*split the matching part of the actual short-code to get
* a list of the values corresponding to the short-code parameter names*/
var valueSplit = theMatch.Split('*');
// Iterate through the list of parameter names
for (int i = 0; i < formatSplit.Length; i++)
{
// If the parameter name is not a constant
if (!formatSplit[i].Equals(valueSplit[i]))
{
// Add the parameter name and value to the list of parameters in the payload
payload.SetDataValue(formatSplit[i], valueSplit[i]);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment