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