forked from aspnet/AspNetWebStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFormatterParameterBinding.cs
More file actions
155 lines (137 loc) · 6.26 KB
/
Copy pathFormatterParameterBinding.cs
File metadata and controls
155 lines (137 loc) · 6.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
// 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;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
using System.Web.Http.Properties;
using System.Web.Http.Validation;
namespace System.Web.Http.ModelBinding
{
/// <summary>
/// Parameter binding that will read from the body and invoke the formatters.
/// </summary>
public class FormatterParameterBinding : HttpParameterBinding
{
// Magic key to pass cancellation token through the request property bag to maintain backward compat.
private const string CancellationTokenKey = "MS_FormatterParameterBinding_CancellationToken";
private IEnumerable<MediaTypeFormatter> _formatters;
private string _errorMessage;
public FormatterParameterBinding(HttpParameterDescriptor descriptor, IEnumerable<MediaTypeFormatter> formatters, IBodyModelValidator bodyModelValidator)
: base(descriptor)
{
if (descriptor.IsOptional)
{
_errorMessage = Error.Format(SRResources.OptionalBodyParameterNotSupported, descriptor.Prefix ?? descriptor.ParameterName, GetType().Name);
}
Formatters = formatters;
BodyModelValidator = bodyModelValidator;
}
public override bool WillReadBody
{
get { return true; }
}
public override string ErrorMessage
{
get
{
return _errorMessage;
}
}
public IEnumerable<MediaTypeFormatter> Formatters
{
get { return _formatters; }
set
{
if (value == null)
{
throw Error.ArgumentNull("formatters");
}
_formatters = value;
}
}
public IBodyModelValidator BodyModelValidator
{
get;
set;
}
public virtual Task<object> ReadContentAsync(HttpRequestMessage request, Type type,
IEnumerable<MediaTypeFormatter> formatters, IFormatterLogger formatterLogger)
{
// Try to get the cancellation token if it is set earlier during the magic handshake
// to maintain backward compatibility.
object cancellationToken;
if (!request.Properties.TryGetValue(CancellationTokenKey, out cancellationToken))
{
cancellationToken = CancellationToken.None;
}
return ReadContentAsync(request, type, formatters, formatterLogger, (CancellationToken)cancellationToken);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposed later")]
public virtual Task<object> ReadContentAsync(HttpRequestMessage request, Type type,
IEnumerable<MediaTypeFormatter> formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
{
HttpContent content = request.Content;
if (content == null)
{
object defaultValue = MediaTypeFormatter.GetDefaultValueForType(type);
if (defaultValue == null)
{
return TaskHelpers.NullResult();
}
else
{
return Task.FromResult(defaultValue);
}
}
try
{
return content.ReadAsAsync(type, formatters, formatterLogger, cancellationToken);
}
catch (UnsupportedMediaTypeException exception)
{
// If there is no Content-Type header, provide a better error message
string errorFormat = content.Headers.ContentType == null ?
SRResources.UnsupportedMediaTypeNoContentType :
SRResources.UnsupportedMediaType;
throw new HttpResponseException(
request.CreateErrorResponse(
HttpStatusCode.UnsupportedMediaType,
Error.Format(errorFormat, exception.MediaType.MediaType),
exception));
}
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext,
CancellationToken cancellationToken)
{
HttpParameterDescriptor paramFromBody = this.Descriptor;
Type type = paramFromBody.ParameterType;
HttpRequestMessage request = actionContext.ControllerContext.Request;
IFormatterLogger formatterLogger = new ModelStateFormatterLogger(actionContext.ModelState, paramFromBody.ParameterName);
return ExecuteBindingAsyncCore(metadataProvider, actionContext, paramFromBody, type, request, formatterLogger, cancellationToken);
}
// Perf-sensitive - keeping the async method as small as possible
private async Task ExecuteBindingAsyncCore(ModelMetadataProvider metadataProvider, HttpActionContext actionContext,
HttpParameterDescriptor paramFromBody, Type type, HttpRequestMessage request, IFormatterLogger formatterLogger,
CancellationToken cancellationToken)
{
// pass the cancellation token through the request as we cannot call the ReadContentAsync overload that takes
// CancellationToken for backword compatibility reasons.
request.Properties[CancellationTokenKey] = cancellationToken;
object model = await ReadContentAsync(request, type, _formatters, formatterLogger);
// Put the parameter result into the action context.
SetValue(actionContext, model);
// validate the object graph.
// null indicates we want no body parameter validation
if (BodyModelValidator != null)
{
BodyModelValidator.Validate(model, type, metadataProvider, actionContext, paramFromBody.ParameterName);
}
}
}
}