diff --git a/src/Umbraco.Core/TypeExtensions.cs b/src/Umbraco.Core/TypeExtensions.cs index 3208c1fbe8..76dc79c219 100644 --- a/src/Umbraco.Core/TypeExtensions.cs +++ b/src/Umbraco.Core/TypeExtensions.cs @@ -425,7 +425,28 @@ namespace Umbraco.Core assemblyName.FullName.StartsWith("App_Code.") ? "App_Code" : assemblyName.Name); } + /// + /// If the given is an array or some other collection + /// comprised of 0 or more instances of a "subtype", get that type + /// + /// the source type + /// + internal static Type GetEnumeratedType(this Type type) + { + if (typeof(IEnumerable).IsAssignableFrom(type) == false) + return null; + // provided by Array + var elType = type.GetElementType(); + if (null != elType) return elType; + + // otherwise provided by collection + var elTypes = type.GetGenericArguments(); + if (elTypes.Length > 0) return elTypes[0]; + + // otherwise is not an 'enumerated' type + return null; + } } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 5e0f5deada..3defb691aa 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -166,24 +166,17 @@ function entityResource($q, $http, umbRequestHelper) { */ getByIds: function (ids, type) { - var query = ""; - _.each(ids, function(item) { - query += "ids=" + item + "&"; - }); - - // if ids array is empty we need a empty variable in the querystring otherwise the service returns a error - if (ids.length === 0) { - query += "ids=&"; - } - - query += "type=" + type; + var query = "type=" + type; return umbRequestHelper.resourcePromise( - $http.get( + $http.post( umbRequestHelper.getApiUrl( "entityApiBaseUrl", "GetByIds", - query)), + query), + { + ids: ids + }), 'Failed to retrieve entity data for ids ' + ids); }, diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 6cdedfd352..40cec56a8e 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -55,7 +55,7 @@ namespace Umbraco.Web.Editors { //we are not also including the Udi[] overload because that is HttpPost only so there won't be any ambiguity controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector( - new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetByIds", "ids", typeof(int[]), typeof(Guid[]) ))); + new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetByIds", "ids", typeof(int[]), typeof(Guid[]), typeof(Udi[]) ))); } } @@ -263,8 +263,12 @@ namespace Umbraco.Web.Editors /// /// /// + /// + /// We allow for POST because there could be quite a lot of Ids + /// [HttpGet] - public IEnumerable GetByIds([FromUri]int[] ids, UmbracoEntityTypes type) + [HttpPost] + public IEnumerable GetByIds([FromJsonPath]int[] ids, UmbracoEntityTypes type) { if (ids == null) { @@ -279,8 +283,12 @@ namespace Umbraco.Web.Editors /// /// /// + /// + /// We allow for POST because there could be quite a lot of Ids + /// [HttpGet] - public IEnumerable GetByIds([FromUri]Guid[] ids, UmbracoEntityTypes type) + [HttpPost] + public IEnumerable GetByIds([FromJsonPath]Guid[] ids, UmbracoEntityTypes type) { if (ids == null) { @@ -288,7 +296,7 @@ namespace Umbraco.Web.Editors } return GetResultForKeys(ids, type); } - + /// /// Get entities by string ids - will try to convert to the correct id type (int, guid, udi) /// @@ -296,8 +304,9 @@ namespace Umbraco.Web.Editors /// /// /// - /// We only allow for POST because there could be quite a lot of Ids + /// We allow for POST because there could be quite a lot of Ids /// + [HttpGet] [HttpPost] public IEnumerable GetByIds([FromJsonPath]Udi[] ids, [FromUri]UmbracoEntityTypes type) { diff --git a/src/Umbraco.Web/Editors/FromJsonPathAttribute.cs b/src/Umbraco.Web/Editors/FromJsonPathAttribute.cs index 3cadd2b6ca..bb275f8fa2 100644 --- a/src/Umbraco.Web/Editors/FromJsonPathAttribute.cs +++ b/src/Umbraco.Web/Editors/FromJsonPathAttribute.cs @@ -1,8 +1,12 @@ +using System.Collections.Generic; using System.Net.Http; +using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; +using System.Web.Http.ValueProviders; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Umbraco.Core; namespace Umbraco.Web.Editors { @@ -23,6 +27,7 @@ namespace Umbraco.Web.Editors internal class FromJsonPathAttribute : ModelBinderAttribute { private readonly string _jsonPath; + private readonly FromUriAttribute _fromUriAttribute = new FromUriAttribute(); public FromJsonPathAttribute() { @@ -33,10 +38,17 @@ namespace Umbraco.Web.Editors _jsonPath = jsonPath; } + public override IEnumerable GetValueProviderFactories(HttpConfiguration configuration) + { + return _fromUriAttribute.GetValueProviderFactories(configuration); + } + public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter) { var config = parameter.Configuration; - var binder = new JsonPathBinder(_jsonPath); + //get the default binder, we'll use that if it's a GET or if the body is empty + var underlyingBinder = base.GetModelBinder(config, parameter.ParameterType); + var binder = new JsonPathBinder(underlyingBinder, _jsonPath); var valueProviderFactories = GetValueProviderFactories(config); return new ModelBinderParameterBinding(parameter, binder, valueProviderFactories); @@ -44,17 +56,30 @@ namespace Umbraco.Web.Editors private class JsonPathBinder : IModelBinder { + private readonly IModelBinder _underlyingBinder; private readonly string _jsonPath; - public JsonPathBinder(string jsonPath) + public JsonPathBinder(IModelBinder underlyingBinder, string jsonPath) { + _underlyingBinder = underlyingBinder; _jsonPath = jsonPath; } public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { + if (actionContext.Request.Method == HttpMethod.Get) + { + return _underlyingBinder.BindModel(actionContext, bindingContext); + } + var requestContent = new HttpMessageContent(actionContext.Request); var strJson = requestContent.HttpRequestMessage.Content.ReadAsStringAsync().Result; + + if (strJson.IsNullOrWhiteSpace()) + { + return _underlyingBinder.BindModel(actionContext, bindingContext); + } + var json = JsonConvert.DeserializeObject(strJson); //if no explicit json path then use the model name diff --git a/src/Umbraco.Web/Editors/ParameterSwapControllerActionSelector.cs b/src/Umbraco.Web/Editors/ParameterSwapControllerActionSelector.cs index 75916f6272..6eb2856912 100644 --- a/src/Umbraco.Web/Editors/ParameterSwapControllerActionSelector.cs +++ b/src/Umbraco.Web/Editors/ParameterSwapControllerActionSelector.cs @@ -42,33 +42,24 @@ namespace Umbraco.Web.Editors if (found != null) { - if (controllerContext.Request.Method == HttpMethod.Get) + HttpActionDescriptor method; + if (TryBindFromUri(controllerContext, found, out method)) { - var requestParam = HttpUtility.ParseQueryString(controllerContext.Request.RequestUri.Query).Get(found.ParamName); - - if (requestParam != null) - { - var paramTypes = found.SupportedTypes; - - foreach (var paramType in paramTypes) - { - var converted = requestParam.TryConvertTo(paramType); - if (converted) - { - var method = MatchByType(paramType, controllerContext, found); - if (method != null) - return method; - } - } - } + return method; } - else if (controllerContext.Request.Method == HttpMethod.Post) - { + //if it's a post we can try to read from the body and bind from th + if (controllerContext.Request.Method == HttpMethod.Post) + { var requestContent = new HttpMessageContent(controllerContext.Request); var strJson = requestContent.HttpRequestMessage.Content.ReadAsStringAsync().Result; var json = JsonConvert.DeserializeObject(strJson); + if (json == null) + { + return base.SelectAction(controllerContext); + } + var requestParam = json[found.ParamName]; if (requestParam != null) @@ -82,7 +73,7 @@ namespace Umbraco.Web.Editors var converted = requestParam.ToObject(paramType); if (converted != null) { - var method = MatchByType(paramType, controllerContext, found); + method = MatchByType(paramType, controllerContext, found); if (method != null) return method; } @@ -102,6 +93,34 @@ namespace Umbraco.Web.Editors return base.SelectAction(controllerContext); } + private bool TryBindFromUri(HttpControllerContext controllerContext, ParameterSwapInfo found, out HttpActionDescriptor method) + { + var requestParam = HttpUtility.ParseQueryString(controllerContext.Request.RequestUri.Query).Get(found.ParamName); + + if (requestParam != null) + { + var paramTypes = found.SupportedTypes; + + foreach (var paramType in paramTypes) + { + //check if this is IEnumerable and if so this will get it's type + //we need to know this since the requestParam will always just be a string + var enumType = paramType.GetEnumeratedType(); + + var converted = requestParam.TryConvertTo(enumType ?? paramType); + if (converted) + { + method = MatchByType(paramType, controllerContext, found); + if (method != null) + return true; + } + } + } + + method = null; + return false; + } + private static ReflectedHttpActionDescriptor MatchByType(Type idType, HttpControllerContext controllerContext, ParameterSwapInfo found) { var controllerType = controllerContext.Controller.GetType();