From 86d4827df211cc28549acb88ee7cb08d6cc4d4aa Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 22 Sep 2017 03:01:40 -0400 Subject: simplify input & output format (#336) --- .../Controllers/ModsController.cs | 22 +++++--- .../Framework/CommaDelimitedModelBinder.cs | 58 ---------------------- .../Framework/CommaDelimitedModelBinderProvider.cs | 27 ---------- .../Framework/ModRepositories/NexusRepository.cs | 6 +-- src/StardewModdingAPI.Web/Models/ModInfoModel.cs | 11 +--- src/StardewModdingAPI.Web/Models/ModSearchModel.cs | 9 ++++ src/StardewModdingAPI.Web/Startup.cs | 16 +----- 7 files changed, 30 insertions(+), 119 deletions(-) delete mode 100644 src/StardewModdingAPI.Web/Framework/CommaDelimitedModelBinder.cs delete mode 100644 src/StardewModdingAPI.Web/Framework/CommaDelimitedModelBinderProvider.cs create mode 100644 src/StardewModdingAPI.Web/Models/ModSearchModel.cs (limited to 'src') diff --git a/src/StardewModdingAPI.Web/Controllers/ModsController.cs b/src/StardewModdingAPI.Web/Controllers/ModsController.cs index d3b49445..876f5248 100644 --- a/src/StardewModdingAPI.Web/Controllers/ModsController.cs +++ b/src/StardewModdingAPI.Web/Controllers/ModsController.cs @@ -29,33 +29,39 @@ namespace StardewModdingAPI.Web.Controllers ** Public methods *********/ /// Fetch version metadata for the given mods. - /// The namespaced mod keys to search. - [HttpGet] - public async Task Post(IEnumerable modKeys) + /// The search options. + [HttpPost] + public async Task> Post([FromBody] ModSearchModel search) { - IList result = new List(); + // sort & filter keys + string[] modKeys = (search.ModKeys ?? new string[0]) + .Distinct(StringComparer.CurrentCultureIgnoreCase) + .OrderBy(p => p, StringComparer.CurrentCultureIgnoreCase) + .ToArray(); + // fetch mod info + IDictionary result = new Dictionary(StringComparer.CurrentCultureIgnoreCase); foreach (string modKey in modKeys) { // parse mod key if (!this.TryParseModKey(modKey, out string vendorKey, out string modID)) { - result.Add(new ModInfoModel(modKey, "The mod key isn't in a valid format. It should contain the mod repository key and mod ID like 'Nexus:541'.")); + result[modKey] = new ModInfoModel("The mod key isn't in a valid format. It should contain the mod repository key and mod ID like 'Nexus:541'."); continue; } // get matching repository if (!this.Repositories.TryGetValue(vendorKey, out IModRepository repository)) { - result.Add(new ModInfoModel(modKey, "There's no mod repository matching this namespaced mod ID.")); + result[modKey] = new ModInfoModel("There's no mod repository matching this namespaced mod ID."); continue; } // fetch mod info - result.Add(await repository.GetModInfoAsync(modID)); + result[modKey] = await repository.GetModInfoAsync(modID); } - return result.ToArray(); + return result; } diff --git a/src/StardewModdingAPI.Web/Framework/CommaDelimitedModelBinder.cs b/src/StardewModdingAPI.Web/Framework/CommaDelimitedModelBinder.cs deleted file mode 100644 index 119b18e6..00000000 --- a/src/StardewModdingAPI.Web/Framework/CommaDelimitedModelBinder.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.ComponentModel; -using System.Reflection; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.ModelBinding; - -namespace StardewModdingAPI.Web.Framework -{ - /// Maps comma-delimited values to an parameter. - /// Derived from . - public class CommaDelimitedModelBinder : IModelBinder - { - /********* - ** Public methods - *********/ - /// Attempts to bind a model. - /// The model binding context. - 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; - } - } -} diff --git a/src/StardewModdingAPI.Web/Framework/CommaDelimitedModelBinderProvider.cs b/src/StardewModdingAPI.Web/Framework/CommaDelimitedModelBinderProvider.cs deleted file mode 100644 index 1b3f0073..00000000 --- a/src/StardewModdingAPI.Web/Framework/CommaDelimitedModelBinderProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc.ModelBinding; - -namespace StardewModdingAPI.Web.Framework -{ - /// Provides comma-delimited model binds for mapping parameters. - /// Derived from . - public class CommaDelimitedModelBinderProvider : IModelBinderProvider - { - /********* - ** Public methods - *********/ - /// Creates a model binder based on the given context. - /// The model binding context. - 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; - } - } -} diff --git a/src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs index 7e3ce4b6..02c2939a 100644 --- a/src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs +++ b/src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs @@ -44,12 +44,12 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories .As(); return response != null - ? new ModInfoModel($"{this.VendorKey}:{id}", response.Name, response.Version, response.Url) - : new ModInfoModel($"{this.VendorKey}:{id}", "Found no mod with this ID."); + ? new ModInfoModel(response.Name, response.Version, response.Url) + : new ModInfoModel("Found no mod with this ID."); } catch (Exception ex) { - return new ModInfoModel($"{this.VendorKey}:{id}", ex.ToString()); + return new ModInfoModel(ex.ToString()); } } diff --git a/src/StardewModdingAPI.Web/Models/ModInfoModel.cs b/src/StardewModdingAPI.Web/Models/ModInfoModel.cs index 723d6c73..180420cd 100644 --- a/src/StardewModdingAPI.Web/Models/ModInfoModel.cs +++ b/src/StardewModdingAPI.Web/Models/ModInfoModel.cs @@ -8,9 +8,6 @@ namespace StardewModdingAPI.Web.Models /********* ** Accessors *********/ - /// The namespaced mod key. - public string ModKey { get; } - /// The mod name. public string Name { get; } @@ -28,15 +25,13 @@ namespace StardewModdingAPI.Web.Models ** Public methods *********/ /// Construct a valid instance. - /// The namespaced mod key. /// The mod name. /// The mod's semantic version number. /// The mod's web URL. /// The error message indicating why the mod is invalid (if applicable). [JsonConstructor] - public ModInfoModel(string modKey, string name, string version, string url, string error = null) + public ModInfoModel(string name, string version, string url, string error = null) { - this.ModKey = modKey; this.Name = name; this.Version = version; this.Url = url; @@ -44,11 +39,9 @@ namespace StardewModdingAPI.Web.Models } /// Construct an valid instance. - /// The namespaced mod key. /// The error message indicating why the mod is invalid. - public ModInfoModel(string modKey, string error) + public ModInfoModel(string error) { - this.ModKey = modKey; this.Error = error; } } diff --git a/src/StardewModdingAPI.Web/Models/ModSearchModel.cs b/src/StardewModdingAPI.Web/Models/ModSearchModel.cs new file mode 100644 index 00000000..b5bd12fb --- /dev/null +++ b/src/StardewModdingAPI.Web/Models/ModSearchModel.cs @@ -0,0 +1,9 @@ +namespace StardewModdingAPI.Web.Models +{ + /// Metadata for mods to look up. + public class ModSearchModel + { + /// The namespaced mod keys to search. + public string[] ModKeys { get; set; } + } +} diff --git a/src/StardewModdingAPI.Web/Startup.cs b/src/StardewModdingAPI.Web/Startup.cs index 2d9a95f1..abae06ec 100644 --- a/src/StardewModdingAPI.Web/Startup.cs +++ b/src/StardewModdingAPI.Web/Startup.cs @@ -1,12 +1,9 @@ -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 Newtonsoft.Json; -using StardewModdingAPI.Web.Framework; namespace StardewModdingAPI.Web { @@ -40,17 +37,8 @@ namespace StardewModdingAPI.Web public void ConfigureServices(IServiceCollection services) { services - .AddMvc(options => - { - // add support for comma-delimited parameters - ArrayModelBinderProvider arrayModelBinderProvider = options.ModelBinderProviders.OfType().First(); - options.ModelBinderProviders.Insert(options.ModelBinderProviders.IndexOf(arrayModelBinderProvider), new CommaDelimitedModelBinderProvider()); - }) - .AddJsonOptions(options => - { - // suppress null values in JSON responses - options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - }); + .AddMvc() + .AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore); // suppress null values in JSON responses } /// The method called by the runtime to configure the HTTP request pipeline. -- cgit