1
1
namespace Microsoft . Web . Http . Dispatcher
2
2
{
3
+ using System ;
4
+ using System . Collections . Generic ;
3
5
using System . Diagnostics . CodeAnalysis ;
4
6
using System . Diagnostics . Contracts ;
7
+ using System . Linq ;
5
8
using System . Net . Http ;
6
9
using System . Web . Http ;
10
+ using System . Web . Http . Controllers ;
7
11
using System . Web . Http . Dispatcher ;
8
12
using System . Web . Http . Tracing ;
9
13
using Versioning ;
10
14
using static ApiVersion ;
11
15
using static System . Net . HttpStatusCode ;
16
+ using static System . String ;
12
17
13
18
internal sealed class HttpResponseExceptionFactory
14
19
{
20
+ const string Allow = nameof ( Allow ) ;
15
21
private static readonly string ControllerSelectorCategory = typeof ( IHttpControllerSelector ) . FullName ;
16
22
private readonly HttpRequestMessage request ;
17
23
@@ -30,8 +36,9 @@ internal HttpResponseException NewNotFoundOrBadRequestException( ControllerSelec
30
36
CreateBadRequest ( conventionRouteResult , directRouteResult ) ?? CreateNotFound ( conventionRouteResult ) ;
31
37
32
38
[ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Created exception cannot be disposed. Handled by the caller." ) ]
33
- internal HttpResponseMessage CreateBadRequestResponse ( ApiVersion requestedVersion ) =>
34
- requestedVersion == null ? CreateBadRequestForUnspecifiedApiVersionOrInvalidApiVersion ( ) : CreateBadRequestForUnsupportedApiVersion ( requestedVersion ) ;
39
+ internal HttpResponseMessage CreateBadRequestResponse ( ApiVersion requestedVersion ) => requestedVersion == null ?
40
+ CreateBadRequestForUnspecifiedApiVersionOrInvalidApiVersion ( versionNeutral : false ) :
41
+ CreateBadRequestForUnsupportedApiVersion ( requestedVersion ) ;
35
42
36
43
[ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Created exception cannot be disposed. Handled by the caller." ) ]
37
44
internal HttpResponseException CreateBadRequest ( ApiVersion requestedVersion ) => new HttpResponseException ( CreateBadRequestResponse ( requestedVersion ) ) ;
@@ -60,17 +67,24 @@ private HttpResponseException CreateBadRequest( ControllerSelectionResult conven
60
67
}
61
68
62
69
[ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Created exception cannot be disposed. Handled by the caller." ) ]
63
- private HttpResponseMessage CreateBadRequestForUnspecifiedApiVersionOrInvalidApiVersion ( )
70
+ private HttpResponseMessage CreateBadRequestForUnspecifiedApiVersionOrInvalidApiVersion ( bool versionNeutral )
64
71
{
65
72
var requestedVersion = request . GetRawRequestedApiVersion ( ) ;
66
73
var parsedVersion = default ( ApiVersion ) ;
67
74
var message = default ( string ) ;
75
+ var context = default ( ErrorResponseContext ) ;
68
76
69
- if ( string . IsNullOrEmpty ( requestedVersion ) )
77
+ if ( IsNullOrEmpty ( requestedVersion ) )
70
78
{
79
+ if ( versionNeutral )
80
+ {
81
+ return null ;
82
+ }
83
+
71
84
message = SR . ApiVersionUnspecified ;
72
85
TraceWriter . Info ( request , ControllerSelectorCategory , message ) ;
73
- return Options . CreateBadRequest ( request , "ApiVersionUnspecified" , message , messageDetail : null ) ;
86
+ context = new ErrorResponseContext ( request , "ApiVersionUnspecified" , message , messageDetail : null ) ;
87
+ return Options . ErrorResponses . BadRequest ( context ) ;
74
88
}
75
89
else if ( TryParse ( requestedVersion , out parsedVersion ) )
76
90
{
@@ -79,10 +93,11 @@ private HttpResponseMessage CreateBadRequestForUnspecifiedApiVersionOrInvalidApi
79
93
80
94
message = SR . VersionedResourceNotSupported . FormatDefault ( request . RequestUri , requestedVersion ) ;
81
95
var messageDetail = SR . VersionedControllerNameNotFound . FormatDefault ( request . RequestUri , requestedVersion ) ;
96
+ context = new ErrorResponseContext ( request , "InvalidApiVersion" , message , messageDetail ) ;
82
97
83
98
TraceWriter . Info ( request , ControllerSelectorCategory , message ) ;
84
99
85
- return Options . CreateBadRequest ( request , "InvalidApiVersion" , message , messageDetail ) ;
100
+ return Options . ErrorResponses . BadRequest ( context ) ;
86
101
}
87
102
88
103
[ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Created exception cannot be disposed. Handled by the caller." ) ]
@@ -93,12 +108,67 @@ private HttpResponseMessage CreateBadRequestForUnsupportedApiVersion( ApiVersion
93
108
94
109
var message = SR . VersionedResourceNotSupported . FormatDefault ( request . RequestUri , requestedVersion ) ;
95
110
var messageDetail = SR . VersionedControllerNameNotFound . FormatDefault ( request . RequestUri , requestedVersion ) ;
111
+ var context = new ErrorResponseContext ( request , "UnsupportedApiVersion" , message , messageDetail ) ;
112
+
113
+ TraceWriter . Info ( request , ControllerSelectorCategory , message ) ;
114
+
115
+ return Options . ErrorResponses . BadRequest ( context ) ;
116
+ }
117
+
118
+ [ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Created exception cannot be disposed. Handled by the caller." ) ]
119
+ internal HttpResponseMessage CreateMethodNotAllowedResponse ( bool versionNeutral , IEnumerable < HttpMethod > allowedMethods )
120
+ {
121
+ Contract . Requires ( allowedMethods != null ) ;
122
+
123
+ var response = CreateBadRequestForUnspecifiedApiVersionOrInvalidApiVersion ( versionNeutral ) ;
124
+
125
+ if ( response != null )
126
+ {
127
+ return response ;
128
+ }
129
+
130
+ var requestedMethod = request . Method ;
131
+ var version = request . GetRequestedApiVersion ( ) ? . ToString ( ) ?? "(null)" ;
132
+ var message = default ( string ) ;
133
+ var messageDetail = default ( string ) ;
134
+
135
+ if ( versionNeutral )
136
+ {
137
+ message = SR . VersionedResourceNotSupported . FormatDefault ( request . RequestUri , version ) ;
138
+ messageDetail = SR . VersionedControllerNameNotFound . FormatDefault ( request . RequestUri , version ) ;
139
+ }
140
+ else
141
+ {
142
+ message = SR . VersionedMethodNotSupported . FormatDefault ( version , requestedMethod ) ;
143
+ messageDetail = SR . VersionedActionNameNotFound . FormatDefault ( request . RequestUri , requestedMethod , version ) ;
144
+ }
96
145
97
146
TraceWriter . Info ( request , ControllerSelectorCategory , message ) ;
98
147
99
- return Options . CreateBadRequest ( request , "UnsupportedApiVersion" , message , messageDetail ) ;
148
+ var context = new ErrorResponseContext ( request , "UnsupportedApiVersion" , message , messageDetail ) ;
149
+
150
+ response = Options . ErrorResponses . MethodNotAllowed ( context ) ;
151
+
152
+ if ( response . Content == null )
153
+ {
154
+ response . Content = new StringContent ( Empty ) ;
155
+ response . Content . Headers . ContentType = null ;
156
+ }
157
+
158
+ var headers = response . Content . Headers ;
159
+
160
+ if ( headers . Allow . Count == 0 )
161
+ {
162
+ headers . Allow . AddRange ( allowedMethods . Select ( m => m . Method ) ) ;
163
+ }
164
+
165
+ return response ;
100
166
}
101
167
168
+ [ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Created exception cannot be disposed. Handled by the caller." ) ]
169
+ internal HttpResponseException NewMethodNotAllowedException ( bool versionNeutral , IEnumerable < HttpMethod > allowedMethods ) =>
170
+ new HttpResponseException ( CreateMethodNotAllowedResponse ( versionNeutral , allowedMethods ) ) ;
171
+
102
172
[ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Created exception cannot be disposed. Handled by the caller." ) ]
103
173
private HttpResponseException CreateNotFound ( ControllerSelectionResult conventionRouteResult )
104
174
{
@@ -107,7 +177,7 @@ private HttpResponseException CreateNotFound( ControllerSelectionResult conventi
107
177
var message = SR . ResourceNotFound . FormatDefault ( request . RequestUri ) ;
108
178
var messageDetail = default ( string ) ;
109
179
110
- if ( string . IsNullOrEmpty ( conventionRouteResult . ControllerName ) )
180
+ if ( IsNullOrEmpty ( conventionRouteResult . ControllerName ) )
111
181
{
112
182
messageDetail = SR . ControllerNameNotFound . FormatDefault ( request . RequestUri ) ;
113
183
}
0 commit comments