From a0f15bba44c2330e2fdb543f099010e71100a7fc Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 11 Dec 2018 11:58:39 +1100 Subject: [PATCH] Fixes binding of FormDataCollection for GET requests which fixes the compatibility with the latest WebApi 5.2.7 version --- .../Trees/ApplicationTreeController.cs | 4 +-- .../Trees/ContentTreeControllerBase.cs | 4 +-- src/Umbraco.Web/Trees/MemberTreeController.cs | 4 +-- src/Umbraco.Web/Trees/TreeControllerBase.cs | 16 ++++------ src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../Filters/HttpQueryStringFilterAttribute.cs | 10 ++---- .../Filters/HttpQueryStringModelBinder.cs | 32 +++++++++++++++++++ 7 files changed, 48 insertions(+), 23 deletions(-) create mode 100644 src/Umbraco.Web/WebApi/Filters/HttpQueryStringModelBinder.cs diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index 31272363ae..eedd840a3e 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using System.Web.Http; +using System.Web.Http.ModelBinding; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Models.Trees; @@ -31,8 +32,7 @@ namespace Umbraco.Web.Trees /// /// An optional bool (defaults to true), if set to false it will also load uninitialized trees /// - [HttpQueryStringFilter("queryStrings")] - public async Task GetApplicationTrees(string application, string tree, FormDataCollection queryStrings, bool onlyInitialized = true) + public async Task GetApplicationTrees(string application, string tree, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings, bool onlyInitialized = true) { application = application.CleanForXss(); diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 9326872111..37125ca73f 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -15,6 +15,7 @@ using Umbraco.Web.WebApi.Filters; using umbraco; using umbraco.BusinessLogic.Actions; using System.Globalization; +using System.Web.Http.ModelBinding; using Umbraco.Core.Services; namespace Umbraco.Web.Trees @@ -30,8 +31,7 @@ namespace Umbraco.Web.Trees /// /// /// - [HttpQueryStringFilter("queryStrings")] - public TreeNode GetTreeNode(string id, FormDataCollection queryStrings) + public TreeNode GetTreeNode(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings) { int asInt; Guid asGuid = Guid.Empty; diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index 3ec12aa2b5..fac2552f99 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -6,6 +6,7 @@ using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Web.Http; +using System.Web.Http.ModelBinding; using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Models; @@ -53,8 +54,7 @@ namespace Umbraco.Web.Trees /// /// /// - [HttpQueryStringFilter("queryStrings")] - public TreeNode GetTreeNode(string id, FormDataCollection queryStrings) + public TreeNode GetTreeNode(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings) { var node = GetSingleTreeNode(id, queryStrings); diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index b0aacc67c6..98eb2ec8bc 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Linq; using System.Net.Http.Formatting; +using System.Web.Http.ModelBinding; using Umbraco.Core; using Umbraco.Core.Events; using Umbraco.Web.Models.Trees; @@ -43,7 +44,7 @@ namespace Umbraco.Web.Trees /// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end /// to the back end to be used in the query for model data. /// - protected abstract TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings); + protected abstract TreeNodeCollection GetTreeNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings); /// /// Returns the menu structure for the node @@ -51,7 +52,7 @@ namespace Umbraco.Web.Trees /// /// /// - protected abstract MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings); + protected abstract MenuItemCollection GetMenuForNode(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings); /// /// The name to display on the root node @@ -68,8 +69,7 @@ namespace Umbraco.Web.Trees /// /// /// - [HttpQueryStringFilter("queryStrings")] - public TreeNode GetRootNode(FormDataCollection queryStrings) + public TreeNode GetRootNode([ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings) { if (queryStrings == null) queryStrings = new FormDataCollection(""); var node = CreateRootNode(queryStrings); @@ -108,8 +108,7 @@ namespace Umbraco.Web.Trees /// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end /// to the back end to be used in the query for model data. /// - [HttpQueryStringFilter("queryStrings")] - public TreeNodeCollection GetNodes(string id, FormDataCollection queryStrings) + public TreeNodeCollection GetNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings) { if (queryStrings == null) queryStrings = new FormDataCollection(""); var nodes = GetTreeNodes(id, queryStrings); @@ -140,8 +139,7 @@ namespace Umbraco.Web.Trees /// /// /// - [HttpQueryStringFilter("queryStrings")] - public MenuItemCollection GetMenu(string id, FormDataCollection queryStrings) + public MenuItemCollection GetMenu(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings) { if (queryStrings == null) queryStrings = new FormDataCollection(""); var menu = GetMenuForNode(id, queryStrings); @@ -390,4 +388,4 @@ namespace Umbraco.Web.Trees if (handler != null) handler(instance, e); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e021de0011..5092c010c2 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -834,6 +834,7 @@ + diff --git a/src/Umbraco.Web/WebApi/Filters/HttpQueryStringFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/HttpQueryStringFilterAttribute.cs index 5cdf7a7e94..5998d54c57 100644 --- a/src/Umbraco.Web/WebApi/Filters/HttpQueryStringFilterAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/HttpQueryStringFilterAttribute.cs @@ -6,13 +6,7 @@ using System.Web.Http.Filters; namespace Umbraco.Web.WebApi.Filters { - /// - /// Allows an Action to execute with an arbitrary number of QueryStrings - /// - /// - /// Just like you can POST an arbitrary number of parameters to an Action, you can't GET an arbitrary number - /// but this will allow you to do it - /// + [Obsolete("Use HttpQueryStringModelBinder to model bind FormDataCollection in a GET request")] public sealed class HttpQueryStringFilterAttribute : ActionFilterAttribute { public string ParameterName { get; private set; } @@ -40,4 +34,4 @@ namespace Umbraco.Web.WebApi.Filters base.OnActionExecuting(actionContext); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/WebApi/Filters/HttpQueryStringModelBinder.cs b/src/Umbraco.Web/WebApi/Filters/HttpQueryStringModelBinder.cs new file mode 100644 index 0000000000..6ffbb239f8 --- /dev/null +++ b/src/Umbraco.Web/WebApi/Filters/HttpQueryStringModelBinder.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Net.Http.Formatting; +using System.Web.Http.Controllers; +using System.Web.Http.ModelBinding; + +namespace Umbraco.Web.WebApi.Filters +{ + /// + /// Allows an Action to execute with an arbitrary number of QueryStrings + /// + /// + /// Just like you can POST an arbitrary number of parameters to an Action, you can't GET an arbitrary number + /// but this will allow you to do it + /// + public sealed class HttpQueryStringModelBinder : IModelBinder + { + public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) + { + //get the query strings from the request properties + if (actionContext.Request.Properties.ContainsKey("MS_QueryNameValuePairs")) + { + if (actionContext.Request.Properties["MS_QueryNameValuePairs"] is IEnumerable> queryStrings) + { + var formData = new FormDataCollection(queryStrings); + bindingContext.Model = formData; + return true; + } + } + return false; + } + } +} \ No newline at end of file