PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/test/System.Web.Http.Test/ModelBinding/DefaultActionValueBinderTest.cs

https://bitbucket.org/mdavid/aspnetwebstack
C# | 490 lines | 348 code | 122 blank | 20 comment | 4 complexity | e351bd82de555c70aba865b91a0e2489 MD5 | raw file
  1. using System.ComponentModel;
  2. using System.Linq;
  3. using System.Net.Http;
  4. using System.Reflection;
  5. using System.Threading;
  6. using System.Web.Http.Controllers;
  7. using System.Web.Http.ValueProviders;
  8. using Xunit;
  9. using Assert = Microsoft.TestCommon.AssertEx;
  10. namespace System.Web.Http.ModelBinding
  11. {
  12. // These tests primarily focus on getting the right binding contract. They don't actually execute the contract.
  13. public class DefaultActionValueBinderTest
  14. {
  15. [Fact]
  16. public void BindValuesAsync_Throws_Null_ActionDescriptor()
  17. {
  18. // Arrange
  19. HttpActionDescriptor actionDescriptor = new ReflectedHttpActionDescriptor { MethodInfo = (MethodInfo)MethodInfo.GetCurrentMethod() };
  20. // Act and Assert
  21. Assert.ThrowsArgumentNull(
  22. () => new DefaultActionValueBinder().GetBinding(null),
  23. "actionDescriptor");
  24. }
  25. private void Action_Int(int id) { }
  26. [Fact]
  27. public void Check_Int_Is_ModelBound()
  28. {
  29. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  30. var binding = binder.GetBinding(GetAction("Action_Int"));
  31. Assert.Equal(1, binding.ParameterBindings.Length);
  32. AssertIsModelBound(binding, 0);
  33. }
  34. private void Action_Int_FromUri([FromUri] int id) { }
  35. [Fact]
  36. public void Check_Explicit_Int_Is_ModelBound()
  37. {
  38. // Even though int is implicitly model bound, still ok to specify it explicitly
  39. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  40. var binding = binder.GetBinding(GetAction("Action_Int_FromUri"));
  41. Assert.Equal(1, binding.ParameterBindings.Length);
  42. AssertIsModelBound(binding, 0);
  43. }
  44. // All types in this signature are model bound
  45. private void Action_SimpleTypes(char ch, Byte b, Int16 i16, UInt16 u16, Int32 i32, UInt32 u32, Int64 i64, UInt64 u64, string s, DateTime d, Decimal dec, Guid g, DateTimeOffset dateTimeOffset, TimeSpan timespan) { }
  46. [Fact]
  47. public void Check_SimpleTypes_Are_ModelBound()
  48. {
  49. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  50. var binding = binder.GetBinding(GetAction("Action_SimpleTypes"));
  51. for(int i = 0; i < binding.ParameterBindings.Length; i++)
  52. {
  53. AssertIsModelBound(binding, 0);
  54. }
  55. }
  56. private void Action_ComplexTypeWithStringConverter(ComplexTypeWithStringConverter x) { }
  57. [Fact]
  58. public void Check_String_TypeConverter_Is_ModelBound()
  59. {
  60. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  61. var binding = binder.GetBinding(GetAction("Action_ComplexTypeWithStringConverter"));
  62. Assert.Equal(1, binding.ParameterBindings.Length);
  63. AssertIsModelBound(binding, 0);
  64. }
  65. private void Action_ComplexTypeWithStringConverter_Body_Override([FromBody] ComplexTypeWithStringConverter x) { }
  66. [Fact]
  67. public void Check_String_TypeConverter_With_Body_Override()
  68. {
  69. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  70. var binding = binder.GetBinding(GetAction("Action_ComplexTypeWithStringConverter_Body_Override"));
  71. Assert.Equal(1, binding.ParameterBindings.Length);
  72. AssertIsBody(binding, 0);
  73. }
  74. private void Action_NullableInt(Nullable<int> id) { }
  75. [Fact]
  76. public void Check_NullableInt_Is_ModelBound()
  77. {
  78. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  79. var binding = binder.GetBinding(GetAction("Action_NullableInt"));
  80. Assert.Equal(1, binding.ParameterBindings.Length);
  81. AssertIsModelBound(binding, 0);
  82. }
  83. private void Action_Nullable_ValueType(Nullable<ComplexValueType> id) { }
  84. [Fact]
  85. public void Check_Nullable_ValueType_Is_FromBody()
  86. {
  87. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  88. var binding = binder.GetBinding(GetAction("Action_Nullable_ValueType"));
  89. Assert.Equal(1, binding.ParameterBindings.Length);
  90. AssertIsBody(binding, 0);
  91. }
  92. private void Action_IntArray(int[] arrayFrombody) { }
  93. [Fact]
  94. public void Check_IntArray_Is_FromBody()
  95. {
  96. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  97. var binding = binder.GetBinding(GetAction("Action_IntArray"));
  98. Assert.Equal(1, binding.ParameterBindings.Length);
  99. AssertIsBody(binding, 0);
  100. }
  101. private void Action_SimpleType_Body([FromBody] int i) { }
  102. [Fact]
  103. public void Check_SimpleType_Body()
  104. {
  105. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  106. var binding = binder.GetBinding(GetAction("Action_SimpleType_Body"));
  107. Assert.Equal(1, binding.ParameterBindings.Length);
  108. AssertIsBody(binding, 0);
  109. }
  110. private void Action_Empty() { }
  111. [Fact]
  112. public void Check_Empty_Action()
  113. {
  114. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  115. var binding = binder.GetBinding(GetAction("Action_Empty"));
  116. Assert.NotNull(binding.ParameterBindings);
  117. Assert.Equal(0, binding.ParameterBindings.Length);
  118. }
  119. private void Action_String_String(string s1, string s2) { }
  120. [Fact]
  121. public void Check_String_String_IsModelBound()
  122. {
  123. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  124. var binding = binder.GetBinding(GetAction("Action_String_String"));
  125. Assert.Equal(2, binding.ParameterBindings.Length);
  126. AssertIsModelBound(binding, 0);
  127. AssertIsModelBound(binding, 1);
  128. }
  129. private void Action_Complex_Type(ComplexType complex) { }
  130. [Fact]
  131. public void Check_Complex_Type_FromBody()
  132. {
  133. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  134. var binding = binder.GetBinding(GetAction("Action_Complex_Type"));
  135. Assert.Equal(1, binding.ParameterBindings.Length);
  136. AssertIsBody(binding, 0);
  137. }
  138. private void Action_Complex_ValueType(ComplexValueType complex) { }
  139. [Fact]
  140. public void Check_Complex_ValueType_FromBody()
  141. {
  142. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  143. var binding = binder.GetBinding(GetAction("Action_Complex_ValueType"));
  144. Assert.Equal(1, binding.ParameterBindings.Length);
  145. AssertIsBody(binding, 0);
  146. }
  147. private void Action_Default_Custom_Model_Binder([ModelBinder] ComplexType complex) { }
  148. [Fact]
  149. public void Check_Customer_Binder()
  150. {
  151. // Mere presence of a ModelBinder attribute means the type is model bound.
  152. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  153. var binding = binder.GetBinding(GetAction("Action_Default_Custom_Model_Binder"));
  154. Assert.Equal(1, binding.ParameterBindings.Length);
  155. AssertIsModelBound(binding, 0);
  156. }
  157. private void Action_Complex_Type_Uri([FromUri] ComplexType complex) { }
  158. [Fact]
  159. public void Check_Complex_Type_FromUri()
  160. {
  161. // [FromUri] is just a specific instance of ModelBinder attribute
  162. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  163. var binding = binder.GetBinding(GetAction("Action_Complex_Type_Uri"));
  164. Assert.Equal(1, binding.ParameterBindings.Length);
  165. AssertIsModelBound(binding, 0);
  166. }
  167. private void Action_Two_Complex_Types(ComplexType complexBody1, ComplexType complexBody2) { }
  168. [Fact]
  169. public void Check_Two_Complex_Types_FromBody()
  170. {
  171. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  172. // It's illegal to have multiple parameters from the body.
  173. // But we should still be able to get a binding for it. We just can't execute it.
  174. var binding = binder.GetBinding(GetAction("Action_Two_Complex_Types"));
  175. Assert.Equal(2, binding.ParameterBindings.Length);
  176. AssertIsError(binding, 0);
  177. AssertIsError(binding, 1);
  178. }
  179. private void Action_Complex_Type_UriAndBody([FromUri] ComplexType complexUri, ComplexType complexBody) { }
  180. [Fact]
  181. public void Check_Complex_Type_FromBody_And_FromUri()
  182. {
  183. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  184. var binding = binder.GetBinding(GetAction("Action_Complex_Type_UriAndBody"));
  185. Assert.Equal(2, binding.ParameterBindings.Length);
  186. AssertIsModelBound(binding, 0);
  187. AssertIsBody(binding, 1);
  188. }
  189. private void Action_CancellationToken(CancellationToken ct) { }
  190. [Fact]
  191. public void Check_Cancellation_Token()
  192. {
  193. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  194. var binding = binder.GetBinding(GetAction("Action_CancellationToken"));
  195. Assert.Equal(1, binding.ParameterBindings.Length);
  196. AssertIsCancellationToken(binding, 0);
  197. }
  198. private void Action_CustomModelBinder_On_Parameter_WithProvider([ModelBinder(typeof(CustomModelBinderProvider))] ComplexType complex) { }
  199. [Fact]
  200. public void Check_CustomModelBinder_On_Parameter()
  201. {
  202. HttpConfiguration config = new HttpConfiguration();
  203. config.ServiceResolver.SetServices(typeof(ValueProviderFactory), new ValueProviderFactory[] {
  204. new CustomValueProviderFactory(),
  205. });
  206. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  207. var binding = binder.GetBinding(GetAction("Action_CustomModelBinder_On_Parameter_WithProvider", config));
  208. Assert.Equal(1, binding.ParameterBindings.Length);
  209. AssertIsModelBound(binding, 0);
  210. ModelBinderParameterBinding p = (ModelBinderParameterBinding) binding.ParameterBindings[0];
  211. Assert.IsType<CustomModelBinderProvider>(p.ModelBinderProvider);
  212. // Since the ModelBinderAttribute didn't specify the valueproviders, we should pull those from config.
  213. Assert.Equal(1, p.ValueProviderFactories.Count());
  214. Assert.IsType<CustomValueProviderFactory>(p.ValueProviderFactories.First());
  215. }
  216. // Model binder attribute is on the type's declaration.
  217. private void Action_ComplexParameter_With_ModelBinder(ComplexTypeWithModelBinder complex) { }
  218. [Fact]
  219. public void Check_Parameter_With_ModelBinder_Attribute_On_Type()
  220. {
  221. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  222. var binding = binder.GetBinding(GetAction("Action_ComplexParameter_With_ModelBinder"));
  223. Assert.Equal(1, binding.ParameterBindings.Length);
  224. AssertIsModelBound(binding, 0);
  225. }
  226. private void Action_Conflicting_Attributes([FromBody][FromUri] int i) { }
  227. [Fact]
  228. public void Error_Conflicting_Attributes()
  229. {
  230. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  231. var binding = binder.GetBinding(GetAction("Action_Conflicting_Attributes"));
  232. // Have 2 attributes that conflict with each other. Still get the contract, but it has an error in it.
  233. Assert.Equal(1, binding.ParameterBindings.Length);
  234. AssertIsError(binding, 0);
  235. }
  236. private void Action_HttpContent_Parameter(HttpContent c) { }
  237. [Fact]
  238. public void Check_HttpContent()
  239. {
  240. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  241. var binding = binder.GetBinding(GetAction("Action_HttpContent_Parameter"));
  242. Assert.Equal(1, binding.ParameterBindings.Length);
  243. AssertIsError(binding, 0);
  244. }
  245. private void Action_Derived_HttpContent_Parameter(StreamContent c) { }
  246. [Fact]
  247. public void Check_Derived_HttpContent()
  248. {
  249. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  250. var binding = binder.GetBinding(GetAction("Action_Derived_HttpContent_Parameter"));
  251. Assert.Equal(1, binding.ParameterBindings.Length);
  252. AssertIsError(binding, 0);
  253. }
  254. private void Action_Request_Parameter(HttpRequestMessage request) { }
  255. [Fact]
  256. public void Check_Request_Parameter()
  257. {
  258. DefaultActionValueBinder binder = new DefaultActionValueBinder();
  259. var binding = binder.GetBinding(GetAction("Action_Request_Parameter"));
  260. Assert.Equal(1, binding.ParameterBindings.Length);
  261. AssertIsCustomBinder<HttpRequestParameterBinding>(binding, 0);
  262. }
  263. // Assert that the binding contract says the given parameter comes from the body
  264. private void AssertIsBody(HttpActionBinding binding, int paramIdx)
  265. {
  266. HttpParameterBinding p = binding.ParameterBindings[paramIdx];
  267. Assert.NotNull(p);
  268. Assert.True(p.IsValid);
  269. Assert.True(p.WillReadBody);
  270. }
  271. // Assert that the binding contract says the given parameter is not from the body (will be handled by model binding)
  272. private void AssertIsModelBound(HttpActionBinding binding, int paramIdx)
  273. {
  274. HttpParameterBinding p = binding.ParameterBindings[paramIdx];
  275. Assert.NotNull(p);
  276. Assert.IsType<ModelBinderParameterBinding>(p);
  277. Assert.True(p.IsValid);
  278. Assert.False(p.WillReadBody);
  279. }
  280. // Assert that the binding contract says the given parameter will be bound to the cancellation token.
  281. private void AssertIsCancellationToken(HttpActionBinding binding, int paramIdx)
  282. {
  283. AssertIsCustomBinder<CancellationTokenParameterBinding>(binding, paramIdx);
  284. }
  285. private void AssertIsError(HttpActionBinding binding, int paramIdx)
  286. {
  287. HttpParameterBinding p = binding.ParameterBindings[paramIdx];
  288. Assert.NotNull(p);
  289. Assert.False(p.IsValid);
  290. Assert.False(p.WillReadBody);
  291. }
  292. private void AssertIsCustomBinder<T>(HttpActionBinding binding, int paramIdx)
  293. {
  294. HttpParameterBinding p = binding.ParameterBindings[paramIdx];
  295. Assert.NotNull(p);
  296. Assert.IsType<T>(p);
  297. Assert.True(p.IsValid);
  298. Assert.False(p.WillReadBody);
  299. }
  300. // Helper to get an ActionDescriptor for a method name.
  301. private HttpActionDescriptor GetAction(string name)
  302. {
  303. return GetAction(name, new HttpConfiguration());
  304. }
  305. private HttpActionDescriptor GetAction(string name, HttpConfiguration config)
  306. {
  307. MethodInfo method = this.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  308. Assert.NotNull(method);
  309. return new ReflectedHttpActionDescriptor { MethodInfo = method, Configuration = config };
  310. }
  311. // Complex type to use with tests
  312. class ComplexType
  313. {
  314. }
  315. struct ComplexValueType
  316. {
  317. }
  318. // Complex type to use with tests
  319. [ModelBinder]
  320. class ComplexTypeWithModelBinder
  321. {
  322. }
  323. // Add Type converter for string, which causes the type to be viewed as a Simple type.
  324. [TypeConverter(typeof(MyTypeConverter))]
  325. public class ComplexTypeWithStringConverter
  326. {
  327. public string Data { get; set; }
  328. public ComplexTypeWithStringConverter(string data)
  329. {
  330. Data = data;
  331. }
  332. }
  333. // A string type converter
  334. public class MyTypeConverter : TypeConverter
  335. {
  336. public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  337. {
  338. if (sourceType == typeof(string))
  339. {
  340. return true;
  341. }
  342. return base.CanConvertFrom(context, sourceType);
  343. }
  344. public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  345. {
  346. if (value is string)
  347. {
  348. return new ComplexTypeWithStringConverter((string)value);
  349. }
  350. return base.ConvertFrom(context, culture, value);
  351. }
  352. }
  353. class CustomModelBinderProvider : ModelBinderProvider
  354. {
  355. public override IModelBinder GetBinder(HttpActionContext actionContext, ModelBindingContext bindingContext)
  356. {
  357. throw new NotImplementedException();
  358. }
  359. }
  360. class CustomValueProviderFactory : ValueProviderFactory
  361. {
  362. public override IValueProvider GetValueProvider(HttpActionContext actionContext)
  363. {
  364. throw new NotImplementedException();
  365. }
  366. }
  367. }
  368. }