Created
January 12, 2019 13:49
-
-
Save markvincze/df1de415418cd58379b42b92470e6f22 to your computer and use it in GitHub Desktop.
Custom route matching in a middleware
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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