Skip to content

Instantly share code, notes, and snippets.

@natenho
Created August 15, 2020 00:25
Show Gist options
  • Save natenho/1d1f9b726af6b419f043497bdd961907 to your computer and use it in GitHub Desktop.
Save natenho/1d1f9b726af6b419f043497bdd961907 to your computer and use it in GitHub Desktop.
Allow a controller method receive both single or multiple models
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc.Formatters
{
/// <summary>
/// Allow a controller method receive both single or multiple models
/// </summary>
/// <seealso cref="http://scottclewell.com/post/multiple-or-single-aspnetcore/"/>
public class SingleOrMultipleInputFormatter<T> : TextInputFormatter
{
public SingleOrMultipleInputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanReadType(Type type)
{
return typeof(IEnumerable<T>).IsAssignableFrom(type);
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
var logger = context.HttpContext.RequestServices.GetService(typeof(ILogger<SingleOrMultipleInputFormatter<T>>)) as ILogger;
var request = context.HttpContext.Request;
using (var reader = context.ReaderFactory(request.Body, encoding))
using (var jsonReader = new JsonTextReader(reader))
{
// Important: Don't close the request stream because you many need it in other middleware or controllers.
jsonReader.CloseInput = false;
var succeeded = true;
object model = null;
try
{
var token = JToken.Load(jsonReader);
if (token.Type == JTokenType.Array)
{
model = token.ToObject<IEnumerable<T>>();
}
else
{
var single = token.ToObject<T>();
model = new[] { single };
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error parsing model");
succeeded = false;
}
if (!succeeded)
{
return await InputFormatterResult.FailureAsync();
}
if (model == null && !context.TreatEmptyInputAsDefaultValue)
{
return await InputFormatterResult.NoValueAsync();
}
return await InputFormatterResult.SuccessAsync(model);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment