As per the title, I would like to be able to add open generic InputFormatter and OutputFormatter instances as part of ConfigureServices in Startup.cs, similarly to how it is possible to add open generic services.
What I would like would look somehitng like this:
services.AddMvc(options =>
{
options.InputFormatters.Add(new ProtobufInputFormatter<>());
options.OutputFormatters.Add(new ProtobufOutputFormatter<>());
options.FormatterMappings.SetMediaTypeMappingForFormat("protobuf", MediaTypeHeaderValue.Parse("application/x-protobuf"));
});
Is this possible in any way?
Edit:
Example of currently implemented ProtobufOutputFormatter
public class ProtobufInputFormatter : InputFormatter
{
static MediaTypeHeaderValue protoMediaType = MediaTypeHeaderValue.Parse("application/x-protobuf");
public override bool CanRead(InputFormatterContext context)
{
var request = context.HttpContext.Request;
MediaTypeHeaderValue requestContentType = null;
MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType);
if (requestContentType == null)
{
return false;
}
return requestContentType.IsSubsetOf(protoMediaType);
}
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
try
{
var request = context.HttpContext.Request;
var obj = (IMessage)Activator.CreateInstance(context.ModelType);
obj.MergeFrom(request.Body);
return InputFormatterResult.SuccessAsync(obj);
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex);
return InputFormatterResult.FailureAsync();
}
}
}
The issue is that using reflection is not as performant as if I were able to use the generic Protobuf deserialiser, eg something like this:
public class ProtobufInputFormatter<T> : InputFormatter where T : IMessage<T>, new()
{
static MediaTypeHeaderValue protoMediaType = MediaTypeHeaderValue.Parse("application/x-protobuf");
public override bool CanRead(InputFormatterContext context)
{
var request = context.HttpContext.Request;
MediaTypeHeaderValue requestContentType = null;
MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType);
if (requestContentType == null)
{
return false;
}
return requestContentType.IsSubsetOf(protoMediaType);
}
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
try
{
var request = context.HttpContext.Request;
var serialiser = new ProtobufSerialiser<T>();
var obj = serialiser.Deserialise(request.Body);
return InputFormatterResult.SuccessAsync(obj);
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex);
return InputFormatterResult.FailureAsync();
}
}
The ProtobufSerialiser is a wrapper around Google.Protobuf we have (implemented to work with a Buffer Pool for performance reasons which is subverted by the need for Activator.CreateInstance as seen in the actual implemented and working non-generic example above.)
The T would come from the endpoint.