Skip to content

Instantly share code, notes, and snippets.

@markvincze
Created January 12, 2019 13:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markvincze/df1de415418cd58379b42b92470e6f22 to your computer and use it in GitHub Desktop.
Save markvincze/df1de415418cd58379b42b92470e6f22 to your computer and use it in GitHub Desktop.
Custom route matching in a middleware
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace CustomMiddlewareExample
{
public class RouteMatcher
{
public RouteValueDictionary Match(string routeTemplate, string requestPath, IQueryCollection query)
{
// The TemplateParser can only parse the route part, and not the query string.
// If the template provided by the user also has a query string, we separate that and match it manually.
var regex = new Regex(@"(.*)(\?[^{}]*$)");
var match = regex.Match(routeTemplate);
if (match.Success)
{
var queryString = match.Groups[2].Value;
routeTemplate = match.Groups[1].Value;
var queryInTemplate = QueryHelpers.ParseQuery(queryString);
if (!query.All(arg => queryInTemplate.ContainsKey(arg.Key.TrimStart('?')) && queryInTemplate[arg.Key.TrimStart('?')] == arg.Value))
{
return null;
}
}
var template = TemplateParser.Parse(routeTemplate);
var matcher = new TemplateMatcher(template, GetDefaults(template));
var values = new RouteValueDictionary();
return matcher.TryMatch(requestPath, values) ? values : null;
}
private RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate)
{
var result = new RouteValueDictionary();
foreach (var parameter in parsedTemplate.Parameters)
{
if (parameter.DefaultValue != null)
{
result.Add(parameter.Name, parameter.DefaultValue);
}
}
return result;
}
}
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// You can do anything custom here, check the HTTP method, etc.
var routeMatcher = new RouteMatcher();
var match = routeMatcher.Match("/my/route/template/{foo}", context.Request.Path, context.Request.Query);
if (match == null)
{
// Route did not match, handle accordingly (return 404, 400, etc.)
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
}
else
{
// The route was matched, use the args
Console.WriteLine("Extracted value: {0}", match["foo"]);
context.Response.StatusCode = (int)HttpStatusCode.OK;
}
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseMiddleware<CustomMiddleware>();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment