forked from aspnet/AspNetWebStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHttpControllerDispatcher.cs
More file actions
228 lines (197 loc) · 8.12 KB
/
HttpControllerDispatcher.cs
File metadata and controls
228 lines (197 loc) · 8.12 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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Http;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Properties;
using System.Web.Http.Routing;
namespace System.Web.Http.Dispatcher
{
/// <summary>
/// Dispatches an incoming <see cref="HttpRequestMessage"/> to an <see cref="IHttpController"/> implementation for processing.
/// </summary>
public class HttpControllerDispatcher : HttpMessageHandler
{
private readonly HttpConfiguration _configuration;
private IExceptionLogger _exceptionLogger;
private IExceptionHandler _exceptionHandler;
private IHttpControllerSelector _controllerSelector;
/// <summary>
/// Initializes a new instance of the <see cref="HttpControllerDispatcher"/> class.
/// </summary>
public HttpControllerDispatcher(HttpConfiguration configuration)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
_configuration = configuration;
}
/// <summary>
/// Gets the <see cref="HttpConfiguration"/>.
/// </summary>
public HttpConfiguration Configuration
{
get { return _configuration; }
}
/// <remarks>This property is internal and settable only for unit testing purposes.</remarks>
internal IExceptionLogger ExceptionLogger
{
get
{
if (_exceptionLogger == null)
{
_exceptionLogger = ExceptionServices.GetLogger(_configuration);
}
return _exceptionLogger;
}
set
{
_exceptionLogger = value;
}
}
/// <remarks>This property is internal and settable only for unit testing purposes.</remarks>
internal IExceptionHandler ExceptionHandler
{
get
{
if (_exceptionHandler == null)
{
_exceptionHandler = ExceptionServices.GetHandler(_configuration);
}
return _exceptionHandler;
}
set
{
_exceptionHandler = value;
}
}
private IHttpControllerSelector ControllerSelector
{
get
{
if (_controllerSelector == null)
{
_controllerSelector = _configuration.Services.GetHttpControllerSelector();
}
return _controllerSelector;
}
}
/// <summary>
/// Dispatches an incoming <see cref="HttpRequestMessage"/> to an <see cref="IHttpController"/>.
/// </summary>
/// <param name="request">The request to dispatch</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{HttpResponseMessage}"/> representing the ongoing operation.</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We report the error in the HTTP response.")]
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
ExceptionDispatchInfo exceptionInfo;
HttpControllerContext controllerContext = null;
try
{
HttpControllerDescriptor controllerDescriptor = ControllerSelector.SelectController(request);
if (controllerDescriptor == null)
{
return request.CreateErrorResponse(
HttpStatusCode.NotFound,
Error.Format(SRResources.ResourceNotFound, request.RequestUri),
SRResources.NoControllerSelected);
}
IHttpController controller = controllerDescriptor.CreateController(request);
if (controller == null)
{
return request.CreateErrorResponse(
HttpStatusCode.NotFound,
Error.Format(SRResources.ResourceNotFound, request.RequestUri),
SRResources.NoControllerCreated);
}
controllerContext = CreateControllerContext(request, controllerDescriptor, controller);
return await controller.ExecuteAsync(controllerContext, cancellationToken);
}
catch (OperationCanceledException)
{
// Propogate the canceled task without calling exception loggers or handlers.
throw;
}
catch (HttpResponseException httpResponseException)
{
return httpResponseException.Response;
}
catch (Exception exception)
{
exceptionInfo = ExceptionDispatchInfo.Capture(exception);
}
Debug.Assert(exceptionInfo.SourceException != null);
ExceptionContext exceptionContext = new ExceptionContext(
exceptionInfo.SourceException,
ExceptionCatchBlocks.HttpControllerDispatcher,
request)
{
ControllerContext = controllerContext,
};
await ExceptionLogger.LogAsync(exceptionContext, cancellationToken);
HttpResponseMessage response = await ExceptionHandler.HandleAsync(exceptionContext, cancellationToken);
if (response == null)
{
exceptionInfo.Throw();
}
return response;
}
private static HttpControllerContext CreateControllerContext(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
IHttpController controller)
{
Contract.Assert(request != null);
Contract.Assert(controllerDescriptor != null);
Contract.Assert(controller != null);
HttpConfiguration controllerConfiguration = controllerDescriptor.Configuration;
// Set the controller configuration on the request properties
HttpConfiguration requestConfig = request.GetConfiguration();
if (requestConfig == null)
{
request.SetConfiguration(controllerConfiguration);
}
else
{
if (requestConfig != controllerConfiguration)
{
request.SetConfiguration(controllerConfiguration);
}
}
HttpRequestContext requestContext = request.GetRequestContext();
// if the host doesn't create the context we will fallback to creating it.
if (requestContext == null)
{
requestContext = new RequestBackedHttpRequestContext(request)
{
// we are caching controller configuration to support per controller configuration.
Configuration = controllerConfiguration,
};
// if the host did not set a request context we will also set it back to the request.
request.SetRequestContext(requestContext);
}
return new HttpControllerContext(requestContext, request, controllerDescriptor, controller);
}
private static HttpConfiguration EnsureNonNull(HttpConfiguration configuration)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
return configuration;
}
}
}