From e319fef5cb39ba1a375545c86f8d8bc2f174ae9a Mon Sep 17 00:00:00 2001 From: Harsh Gupta Date: Thu, 9 Oct 2014 12:41:03 -0700 Subject: [PATCH] Adding CancellationTokenModelBinder. --- .../Binders/CancellationTokenModelBinder.cs | 26 ++++++++ src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs | 1 + .../ModelBindingTests.cs | 31 +++++++++ .../CancellationTokenModelBinderTests.cs | 63 +++++++++++++++++++ .../MvcOptionSetupTest.cs | 11 ++-- .../Controllers/HomeController.cs | 18 +++++- 6 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CancellationTokenModelBinder.cs create mode 100644 test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CancellationTokenModelBinderTests.cs diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CancellationTokenModelBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CancellationTokenModelBinder.cs new file mode 100644 index 0000000000..a9671a9e9b --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CancellationTokenModelBinder.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// Represents a model binder which can bind models of type . + /// + public class CancellationTokenModelBinder : IModelBinder + { + /// + public Task BindModelAsync(ModelBindingContext bindingContext) + { + if (bindingContext.ModelType == typeof(CancellationToken)) + { + bindingContext.Model = bindingContext.HttpContext.RequestAborted; + return Task.FromResult(true); + } + + return Task.FromResult(false); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs index 3959a57f92..9c449a294b 100644 --- a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs @@ -27,6 +27,7 @@ namespace Microsoft.AspNet.Mvc // Set up ModelBinding options.ModelBinders.Add(new TypeConverterModelBinder()); options.ModelBinders.Add(new TypeMatchModelBinder()); + options.ModelBinders.Add(new CancellationTokenModelBinder()); options.ModelBinders.Add(new ByteArrayModelBinder()); options.ModelBinders.Add(typeof(GenericModelBinder)); options.ModelBinders.Add(new MutableObjectModelBinder()); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTests.cs index 6e8d99f9c5..f120d7e3d5 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTests.cs @@ -18,6 +18,37 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests private readonly IServiceProvider _services = TestHelper.CreateServices("ModelBindingWebSite"); private readonly Action _app = new ModelBindingWebSite.Startup().Configure; + [Fact] + public async Task ModelBindCancellationTokenParameteres() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/Home/ActionWithCancellationToken"); + + //Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("true", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task ModelBindCancellationToken_ForProperties() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync( + "http://localhost/Home/ActionWithCancellationTokenModel?wrapper=bogusValue"); + + //Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("true", await response.Content.ReadAsStringAsync()); + } + [Fact] public async Task ModelBindingBindsBase64StringsToByteArrays() { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CancellationTokenModelBinderTests.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CancellationTokenModelBinderTests.cs new file mode 100644 index 0000000000..b2322de320 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CancellationTokenModelBinderTests.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.PipelineCore; +using Xunit; + +namespace Microsoft.AspNet.Mvc.ModelBinding.Test +{ + public class CancellationTokenModelBinderTests + { + [Fact] + public async Task CancellationTokenModelBinder_ReturnsTrue_ForCancellationTokenType() + { + // Arrange + var bindingContext = GetBindingContext(typeof(CancellationToken)); + var binder = new CancellationTokenModelBinder(); + + // Act + var bound = await binder.BindModelAsync(bindingContext); + + // Assert + Assert.True(bound); + Assert.Equal(bindingContext.HttpContext.RequestAborted, bindingContext.Model); + } + + [Theory] + [InlineData(typeof(int))] + [InlineData(typeof(object))] + [InlineData(typeof(CancellationTokenModelBinderTests))] + public async Task CancellationTokenModelBinder_ReturnsFalse_ForNonCancellationTokenType(Type t) + { + // Arrange + var bindingContext = GetBindingContext(t); + var binder = new CancellationTokenModelBinder(); + + // Act + var bound = await binder.BindModelAsync(bindingContext); + + // Assert + Assert.False(bound); + Assert.Null(bindingContext.Model); + } + + private static ModelBindingContext GetBindingContext(Type modelType) + { + var metadataProvider = new EmptyModelMetadataProvider(); + ModelBindingContext bindingContext = new ModelBindingContext + { + ModelMetadata = metadataProvider.GetMetadataForType(null, modelType), + ModelName = "someName", + ValueProvider = new SimpleHttpValueProvider(), + ModelBinder = new CancellationTokenModelBinder(), + MetadataProvider = metadataProvider, + HttpContext = new DefaultHttpContext(), + }; + + return bindingContext; + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs index c95f0a8244..5ce47d0865 100644 --- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs @@ -35,13 +35,14 @@ namespace Microsoft.AspNet.Mvc setup.Invoke(mvcOptions); // Assert - Assert.Equal(6, mvcOptions.ModelBinders.Count); + Assert.Equal(7, mvcOptions.ModelBinders.Count); Assert.Equal(typeof(TypeConverterModelBinder), mvcOptions.ModelBinders[0].OptionType); Assert.Equal(typeof(TypeMatchModelBinder), mvcOptions.ModelBinders[1].OptionType); - Assert.Equal(typeof(ByteArrayModelBinder), mvcOptions.ModelBinders[2].OptionType); - Assert.Equal(typeof(GenericModelBinder), mvcOptions.ModelBinders[3].OptionType); - Assert.Equal(typeof(MutableObjectModelBinder), mvcOptions.ModelBinders[4].OptionType); - Assert.Equal(typeof(ComplexModelDtoModelBinder), mvcOptions.ModelBinders[5].OptionType); + Assert.Equal(typeof(CancellationTokenModelBinder), mvcOptions.ModelBinders[2].OptionType); + Assert.Equal(typeof(ByteArrayModelBinder), mvcOptions.ModelBinders[3].OptionType); + Assert.Equal(typeof(GenericModelBinder), mvcOptions.ModelBinders[4].OptionType); + Assert.Equal(typeof(MutableObjectModelBinder), mvcOptions.ModelBinders[5].OptionType); + Assert.Equal(typeof(ComplexModelDtoModelBinder), mvcOptions.ModelBinders[6].OptionType); } [Fact] diff --git a/test/WebSites/ModelBindingWebSite/Controllers/HomeController.cs b/test/WebSites/ModelBindingWebSite/Controllers/HomeController.cs index 5284020409..a6ff4da7f7 100644 --- a/test/WebSites/ModelBindingWebSite/Controllers/HomeController.cs +++ b/test/WebSites/ModelBindingWebSite/Controllers/HomeController.cs @@ -2,8 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using Microsoft.AspNet.Mvc; using System.Linq; +using System.Threading; +using Microsoft.AspNet.Mvc; using ModelBindingWebSite.Models; namespace ModelBindingWebSite.Controllers @@ -26,6 +27,16 @@ namespace ModelBindingWebSite.Controllers return CreateValidationDictionary(); } + public bool ActionWithCancellationToken(CancellationToken token) + { + return token == ActionContext.HttpContext.RequestAborted; + } + + public bool ActionWithCancellationTokenModel(CancellationTokenModel wrapper) + { + return wrapper.CancellationToken == ActionContext.HttpContext.RequestAborted; + } + private Dictionary CreateValidationDictionary() { var result = new Dictionary(); @@ -42,5 +53,10 @@ namespace ModelBindingWebSite.Controllers return result; } + + public class CancellationTokenModel + { + public CancellationToken CancellationToken { get; set; } + } } } \ No newline at end of file