diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/HttpRequest.cs b/src/Microsoft.AspNetCore.Http.Abstractions/HttpRequest.cs
index 3488bf3bb5..4c4d0d1af1 100644
--- a/src/Microsoft.AspNetCore.Http.Abstractions/HttpRequest.cs
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/HttpRequest.cs
@@ -4,6 +4,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Routing;
namespace Microsoft.AspNetCore.Http
{
@@ -117,5 +118,11 @@ namespace Microsoft.AspNetCore.Http
///
///
public abstract Task ReadFormAsync(CancellationToken cancellationToken = new CancellationToken());
+
+ ///
+ /// Gets the collection of route values for this request.
+ ///
+ /// The collection of route values for this request.
+ public virtual RouteValueDictionary RouteValues { get; set; }
}
}
diff --git a/src/Microsoft.AspNetCore.Http/Features/RouteValuesFeature.cs b/src/Microsoft.AspNetCore.Http/Features/RouteValuesFeature.cs
new file mode 100644
index 0000000000..e4a459e991
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Http/Features/RouteValuesFeature.cs
@@ -0,0 +1,34 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Routing;
+
+namespace Microsoft.AspNetCore.Http.Features
+{
+ ///
+ /// A feature for routing values. Use
+ /// to access the values associated with the current request.
+ ///
+ public class RouteValuesFeature : IRouteValuesFeature
+ {
+ private RouteValueDictionary _routeValues;
+
+ ///
+ /// Gets or sets the associated with the currrent
+ /// request.
+ ///
+ public RouteValueDictionary RouteValues
+ {
+ get
+ {
+ if (_routeValues == null)
+ {
+ _routeValues = new RouteValueDictionary();
+ }
+
+ return _routeValues;
+ }
+ set => _routeValues = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Http/Internal/DefaultHttpRequest.cs b/src/Microsoft.AspNetCore.Http/Internal/DefaultHttpRequest.cs
index f216475db6..e2512f60dc 100644
--- a/src/Microsoft.AspNetCore.Http/Internal/DefaultHttpRequest.cs
+++ b/src/Microsoft.AspNetCore.Http/Internal/DefaultHttpRequest.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Routing;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Http.Internal
@@ -17,6 +18,7 @@ namespace Microsoft.AspNetCore.Http.Internal
private readonly static Func _newQueryFeature = f => new QueryFeature(f);
private readonly static Func _newFormFeature = r => new FormFeature(r);
private readonly static Func _newRequestCookiesFeature = f => new RequestCookiesFeature(f);
+ private readonly static Func _newRouteValuesFeature = f => new RouteValuesFeature();
private HttpContext _context;
private FeatureReferences _features;
@@ -52,6 +54,9 @@ namespace Microsoft.AspNetCore.Http.Internal
private IRequestCookiesFeature RequestCookiesFeature =>
_features.Fetch(ref _features.Cache.Cookies, _newRequestCookiesFeature);
+ private IRouteValuesFeature RouteValuesFeature =>
+ _features.Fetch(ref _features.Cache.RouteValues, _newRouteValuesFeature);
+
public override PathString PathBase
{
get { return new PathString(HttpRequestFeature.PathBase); }
@@ -151,12 +156,19 @@ namespace Microsoft.AspNetCore.Http.Internal
return FormFeature.ReadFormAsync(cancellationToken);
}
+ public override RouteValueDictionary RouteValues
+ {
+ get { return RouteValuesFeature.RouteValues; }
+ set { RouteValuesFeature.RouteValues = value; }
+ }
+
struct FeatureInterfaces
{
public IHttpRequestFeature Request;
public IQueryFeature Query;
public IFormFeature Form;
public IRequestCookiesFeature Cookies;
+ public IRouteValuesFeature RouteValues;
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Http.Tests/Internal/DefaultHttpRequestTests.cs b/test/Microsoft.AspNetCore.Http.Tests/Internal/DefaultHttpRequestTests.cs
index dbe1d54dd0..09e47a962e 100644
--- a/test/Microsoft.AspNetCore.Http.Tests/Internal/DefaultHttpRequestTests.cs
+++ b/test/Microsoft.AspNetCore.Http.Tests/Internal/DefaultHttpRequestTests.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Primitives;
using Xunit;
@@ -194,6 +195,57 @@ namespace Microsoft.AspNetCore.Http.Internal
Assert.Equal(new[] { "name2=value2" }, cookieHeaders);
}
+ [Fact]
+ public void RouteValues_GetAndSet()
+ {
+ var context = new DefaultHttpContext();
+ var request = context.Request;
+
+ var routeValuesFeature = context.Features.Get();
+ // No feature set for initial DefaultHttpRequest
+ Assert.Null(routeValuesFeature);
+
+ // Route values returns empty collection by default
+ Assert.Empty(request.RouteValues);
+
+ // Get and set value on request route values
+ request.RouteValues["new"] = "setvalue";
+ Assert.Equal("setvalue", request.RouteValues["new"]);
+
+ routeValuesFeature = context.Features.Get();
+ // Accessing DefaultHttpRequest.RouteValues creates feature
+ Assert.NotNull(routeValuesFeature);
+
+ request.RouteValues = new RouteValueDictionary(new { key = "value" });
+ // Can set DefaultHttpRequest.RouteValues
+ Assert.NotNull(request.RouteValues);
+ Assert.Equal("value", request.RouteValues["key"]);
+
+ // DefaultHttpRequest.RouteValues uses feature
+ Assert.Equal(routeValuesFeature.RouteValues, request.RouteValues);
+
+ // Setting route values to null sets empty collection on request
+ routeValuesFeature.RouteValues = null;
+ Assert.Empty(request.RouteValues);
+
+ var customRouteValuesFeature = new CustomRouteValuesFeature
+ {
+ RouteValues = new RouteValueDictionary(new { key = "customvalue" })
+ };
+ context.Features.Set(customRouteValuesFeature);
+ // Can override DefaultHttpRequest.RouteValues with custom feature
+ Assert.Equal(customRouteValuesFeature.RouteValues, request.RouteValues);
+
+ // Can clear feature
+ context.Features.Set(null);
+ Assert.Empty(request.RouteValues);
+ }
+
+ private class CustomRouteValuesFeature : IRouteValuesFeature
+ {
+ public RouteValueDictionary RouteValues { get; set; }
+ }
+
private static HttpRequest CreateRequest(IHeaderDictionary headers)
{
var context = new DefaultHttpContext();