forked from aspnet/AspNetWebStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHttpWebRoute.cs
More file actions
183 lines (162 loc) · 8.26 KB
/
HttpWebRoute.cs
File metadata and controls
183 lines (162 loc) · 8.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// 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 System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Routing;
using System.Web.Http.WebHost.Properties;
using System.Web.Routing;
namespace System.Web.Http.WebHost.Routing
{
/// <summary>
/// Mimics the System.Web.Routing.Route class to work better for Web API scenarios. The only
/// difference between the base class and this class is that this one will match only when
/// a special "httproute" key is specified when generating URLs. There is no special behavior
/// for incoming URLs.
/// </summary>
internal class HttpWebRoute : Route
{
/// <summary>
/// Key used to signify that a route URL generation request should include HTTP routes (e.g. Web API).
/// If this key is not specified then no HTTP routes will match.
/// </summary>
internal const string HttpRouteKey = "httproute";
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "Matches the base class's parameter names.")]
public HttpWebRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler, IHttpRoute httpRoute)
: base(url, defaults, constraints, dataTokens, routeHandler)
{
if (httpRoute == null)
{
throw Error.ArgumentNull("httpRoute");
}
HttpRoute = httpRoute;
}
/// <summary>
/// Gets the <see cref="IHttpRoute"/> associated with this <see cref="HttpWebRoute"/>.
/// </summary>
public IHttpRoute HttpRoute { get; private set; }
protected override bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
// The base class will validate that a constraint is either a string or IRoutingConstraint inside its
// ProcessConstraint method. We're doing the validation up front here because we also support
// IHttpRouteConstraint and we want the error message to reflect all three valid possibilities.
ValidateConstraint(HttpRoute.RouteTemplate, parameterName, constraint);
IHttpRouteConstraint httpRouteConstraint = constraint as IHttpRouteConstraint;
if (httpRouteConstraint != null)
{
HttpRequestMessage request = httpContext.GetOrCreateHttpRequestMessage();
return httpRouteConstraint.Match(request, HttpRoute, parameterName, values, ConvertRouteDirection(routeDirection));
}
return base.ProcessConstraint(httpContext, constraint, parameterName, values, routeDirection);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Top-level catch block for unhandled routing exceptions.")]
public override RouteData GetRouteData(HttpContextBase httpContext)
{
try
{
if (HttpRoute is HostedHttpRoute)
{
return base.GetRouteData(httpContext);
}
else
{
// if user passed us a custom IHttpRoute, then we should invoke their function instead of the base
HttpRequestMessage request = httpContext.GetOrCreateHttpRequestMessage();
IHttpRouteData data = HttpRoute.GetRouteData(httpContext.Request.ApplicationPath, request);
return data == null ? null : data.ToRouteData();
}
}
catch (Exception exception)
{
// Processing an exception involves async work, and this method is synchronous.
// Instead of waiting on the async work here, it's better to return a handler that will deal with the
// exception asynchronously during its request processing method.
ExceptionDispatchInfo exceptionInfo = ExceptionDispatchInfo.Capture(exception);
return new RouteData(this, new HttpRouteExceptionRouteHandler(exceptionInfo));
}
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
// Only perform URL generation if the "httproute" key was specified. This allows these
// routes to be ignored when a regular MVC app tries to generate URLs. Without this special
// key an HTTP route used for Web API would normally take over almost all the routes in a
// typical app.
if (!values.ContainsKey(HttpRouteKey))
{
return null;
}
// Remove the value from the collection so that it doesn't affect the generated URL
RouteValueDictionary newValues = GetRouteDictionaryWithoutHttpRouteKey(values);
if (HttpRoute is HostedHttpRoute)
{
return base.GetVirtualPath(requestContext, newValues);
}
else
{
// if user passed us a custom IHttpRoute, then we should invoke their function instead of the base
HttpRequestMessage request = requestContext.HttpContext.GetOrCreateHttpRequestMessage();
IHttpVirtualPathData virtualPathData = HttpRoute.GetVirtualPath(request, values);
return virtualPathData == null ? null : new VirtualPathData(this, virtualPathData.VirtualPath);
}
}
private static RouteValueDictionary GetRouteDictionaryWithoutHttpRouteKey(IDictionary<string, object> routeValues)
{
var newRouteValues = new RouteValueDictionary();
foreach (var routeValue in routeValues)
{
if (!String.Equals(routeValue.Key, HttpRouteKey, StringComparison.OrdinalIgnoreCase))
{
newRouteValues.Add(routeValue.Key, routeValue.Value);
}
}
return newRouteValues;
}
private static HttpRouteDirection ConvertRouteDirection(RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.IncomingRequest)
{
return HttpRouteDirection.UriResolution;
}
if (routeDirection == RouteDirection.UrlGeneration)
{
return HttpRouteDirection.UriGeneration;
}
throw Error.InvalidEnumArgument("routeDirection", (int)routeDirection, typeof(RouteDirection));
}
// Validates that this constraint is of a type that HttpWebRoute can process. This is not valid to
// call when a route inherits from HttpWebRoute - as the derived class can handle any types of
// constraints it wants to support.
internal static void ValidateConstraint(string routeTemplate, string name, object constraint)
{
if (constraint is IHttpRouteConstraint)
{
return;
}
// This validation is repeated in the call to base.ProcessConstraint, but if we do it here we can give a
// better error message. base.ProcessConstraint doesn't handle IHttpRouteConstraint, but this class does.
if (constraint is IRouteConstraint)
{
return;
}
if (constraint is string)
{
return;
}
throw CreateInvalidConstraintTypeException(routeTemplate, name);
}
private static Exception CreateInvalidConstraintTypeException(string routeTemplate, string name)
{
return Error.InvalidOperation(
SRResources.Route_ValidationMustBeStringOrCustomConstraint,
name,
routeTemplate,
typeof(IHttpRouteConstraint).FullName,
typeof(IRouteConstraint).FullName);
}
}
}