Gets FromJsonPath working for EntityController to have GetByIds with all types of Ids for both GET and POST

This commit is contained in:
Shannon
2017-01-31 19:11:05 +11:00
parent 8d598cd37c
commit e95cb14d48
5 changed files with 108 additions and 41 deletions

View File

@@ -425,7 +425,28 @@ namespace Umbraco.Core
assemblyName.FullName.StartsWith("App_Code.") ? "App_Code" : assemblyName.Name);
}
/// <summary>
/// If the given <paramref name="type"/> is an array or some other collection
/// comprised of 0 or more instances of a "subtype", get that type
/// </summary>
/// <param name="type">the source type</param>
/// <returns></returns>
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;
}
}
}

View File

@@ -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);
},

View File

@@ -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
/// <param name="ids"></param>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>
/// We allow for POST because there could be quite a lot of Ids
/// </remarks>
[HttpGet]
public IEnumerable<EntityBasic> GetByIds([FromUri]int[] ids, UmbracoEntityTypes type)
[HttpPost]
public IEnumerable<EntityBasic> GetByIds([FromJsonPath]int[] ids, UmbracoEntityTypes type)
{
if (ids == null)
{
@@ -279,8 +283,12 @@ namespace Umbraco.Web.Editors
/// <param name="ids"></param>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>
/// We allow for POST because there could be quite a lot of Ids
/// </remarks>
[HttpGet]
public IEnumerable<EntityBasic> GetByIds([FromUri]Guid[] ids, UmbracoEntityTypes type)
[HttpPost]
public IEnumerable<EntityBasic> GetByIds([FromJsonPath]Guid[] ids, UmbracoEntityTypes type)
{
if (ids == null)
{
@@ -288,7 +296,7 @@ namespace Umbraco.Web.Editors
}
return GetResultForKeys(ids, type);
}
/// <summary>
/// Get entities by string ids - will try to convert to the correct id type (int, guid, udi)
/// </summary>
@@ -296,8 +304,9 @@ namespace Umbraco.Web.Editors
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>
/// 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
/// </remarks>
[HttpGet]
[HttpPost]
public IEnumerable<EntityBasic> GetByIds([FromJsonPath]Udi[] ids, [FromUri]UmbracoEntityTypes type)
{

View File

@@ -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<ValueProviderFactory> 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<JObject>(strJson);
//if no explicit json path then use the model name

View File

@@ -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<JObject>(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();