switch mod update endpoint to GET with comma-delimited mod keys (#336)

This commit is contained in:
Jesse Plamondon-Willard 2017-09-22 01:57:18 -04:00
parent ef60b8d32a
commit 86e5559678
5 changed files with 100 additions and 15 deletions

View File

@ -29,13 +29,13 @@ namespace StardewModdingAPI.Web.Controllers
** Public methods
*********/
/// <summary>Fetch version metadata for the given mods.</summary>
/// <param name="search">The mod update search criteria.</param>
[HttpPost]
public async Task<ModInfoModel[]> Post([FromBody] ModSearchModel search)
/// <param name="modKeys">The namespaced mod keys to search.</param>
[HttpGet]
public async Task<ModInfoModel[]> Post(IEnumerable<string> modKeys)
{
IList<ModInfoModel> result = new List<ModInfoModel>();
foreach (string modKey in search.ModKeys)
foreach (string modKey in modKeys)
{
// parse mod key
if (!this.TryParseModKey(modKey, out string vendorKey, out string modID))

View File

@ -0,0 +1,58 @@
using System;
using System.ComponentModel;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace StardewModdingAPI.Web.Framework
{
/// <summary>Maps comma-delimited values to an <see cref="System.Collections.Generic.IEnumerable{T}"/> parameter.</summary>
/// <remarks>Derived from <a href="https://stackoverflow.com/a/43655986/262123" />.</remarks>
public class CommaDelimitedModelBinder : IModelBinder
{
/*********
** Public methods
*********/
/// <summary>Attempts to bind a model.</summary>
/// <param name="bindingContext">The model binding context.</param>
public Task BindModelAsync(ModelBindingContext bindingContext)
{
// validate
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
// extract values
string modelName = bindingContext.ModelName;
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
string[] values = valueProviderResult
.ToString()
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Type elementType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];
if (values.Length == 0)
{
bindingContext.Result = ModelBindingResult.Success(Array.CreateInstance(elementType, 0));
return Task.CompletedTask;
}
// map values
TypeConverter converter = TypeDescriptor.GetConverter(elementType);
Array typedArray = Array.CreateInstance(elementType, values.Length);
try
{
for (int i = 0; i < values.Length; ++i)
{
string value = values[i];
object convertedValue = converter.ConvertFromString(value);
typedArray.SetValue(convertedValue, i);
}
}
catch (Exception exception)
{
bindingContext.ModelState.TryAddModelError(modelName, exception, bindingContext.ModelMetadata);
}
bindingContext.Result = ModelBindingResult.Success(typedArray);
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace StardewModdingAPI.Web.Framework
{
/// <summary>Provides comma-delimited model binds for mapping parameters.</summary>
/// <remarks>Derived from <a href="https://stackoverflow.com/a/43655986/262123" />.</remarks>
public class CommaDelimitedModelBinderProvider : IModelBinderProvider
{
/*********
** Public methods
*********/
/// <summary>Creates a model binder based on the given context.</summary>
/// <param name="context">The model binding context.</param>
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
// validate
if (context == null)
throw new ArgumentNullException(nameof(context));
// get model binder
return context.Metadata.IsEnumerableType && !context.Metadata.ElementMetadata.IsComplexType
? new CommaDelimitedModelBinder()
: null;
}
}
}

View File

@ -1,9 +0,0 @@
namespace StardewModdingAPI.Web.Models
{
/// <summary>The mod update search criteria.</summary>
public class ModSearchModel
{
/// <summary>The namespaced mod keys to search.</summary>
public string[] ModKeys { get; set; }
}
}

View File

@ -1,8 +1,11 @@
using Microsoft.AspNetCore.Builder;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StardewModdingAPI.Web.Framework;
namespace StardewModdingAPI.Web
{
@ -35,7 +38,13 @@ namespace StardewModdingAPI.Web
/// <param name="services">The service injection container.</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services
.AddMvc(options =>
{
// add support for comma-delimited parameters
ArrayModelBinderProvider arrayModelBinderProvider = options.ModelBinderProviders.OfType<ArrayModelBinderProvider>().First();
options.ModelBinderProviders.Insert(options.ModelBinderProviders.IndexOf(arrayModelBinderProvider), new CommaDelimitedModelBinderProvider());
});
}
/// <summary>The method called by the runtime to configure the HTTP request pipeline.</summary>