Reputation: 24181
I want to have a custom attribute to parse data as stream and be testable with Swagger.
So I created controller which reads from POST
body:
[SwaggerOperation("Create")]
[SwaggerResponse(HttpStatusCode.Created)]
public async Task<string> Post([FromContent]Stream contentStream)
{
using (StreamReader reader = new StreamReader(contentStream, Encoding.UTF8))
{
var str = reader.ReadToEnd();
Console.WriteLine(str);
}
return "OK";
}
How to define stream so it is visible in Swagger UI?
Here is my implementation of FromContent
attribute and ContentParameterBinding
binding:
public class ContentParameterBinding : HttpParameterBinding
{
private struct AsyncVoid{}
public ContentParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor)
{
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext,
CancellationToken cancellationToken)
{
var binding = actionContext.ActionDescriptor.ActionBinding;
if (binding.ParameterBindings.Length > 1 ||
actionContext.Request.Method == HttpMethod.Get)
{
var taskSource = new TaskCompletionSource<AsyncVoid>();
taskSource.SetResult(default(AsyncVoid));
return taskSource.Task as Task;
}
var type = binding.ParameterBindings[0].Descriptor.ParameterType;
if (type == typeof(HttpContent))
{
SetValue(actionContext, actionContext.Request.Content);
var tcs = new TaskCompletionSource<object>();
tcs.SetResult(actionContext.Request.Content);
return tcs.Task;
}
if (type == typeof(Stream))
{
return actionContext.Request.Content
.ReadAsStreamAsync()
.ContinueWith((task) =>
{
SetValue(actionContext, task.Result);
});
}
throw new InvalidOperationException("Only HttpContent and Stream are supported for [FromContent] parameters");
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public sealed class FromContentAttribute : ParameterBindingAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
if (parameter == null)
throw new ArgumentException("Invalid parameter");
return new ContentParameterBinding(parameter);
}
}
Update
When I create Stream
using [FromBody]
is shows correctly in Swagger, however Stream
is not initiated and ==null
[SwaggerOperation("Create")]
[SwaggerResponse(HttpStatusCode.Created)]
public async Task<string> Post([FromBody]Stream contentStream)
{
using (StreamReader reader = new StreamReader(contentStream, Encoding.UTF8))
{
var str = reader.ReadToEnd();
Console.WriteLine(str);
}
return "OK";
}
So I want to have the same UI but with my custom attribute which let's me have Stream
from content.
With my custom attribute it shows without TextArea
for the parameter but could be tested using Postman and work correctly and Stream
is available
Upvotes: 3
Views: 1532
Reputation: 2240
Try implementing the interface IValueProviderParameterBinding
:
public class ContentParameterBinding
: HttpParameterBinding, IValueProviderParameterBinding
{
public IEnumerable<ValueProviderFactory> ValueProviderFactories
{
get
{
return this.Descriptor.Configuration.Services.GetValueProviderFactories();
}
}
}
In my case it helped.
Also it's generally cleaner as it doesn't inherit FormatterParameterBinding
logic, which may not be required.
Upvotes: 1
Reputation: 6090
Inherit your binding from FormatterParameterBinding class:
public class ContentParameterBinding : FormatterParameterBinding
{
public ContentParameterBinding(HttpParameterDescriptor descriptor)
: base(descriptor,
descriptor.Configuration.Formatters,
descriptor.Configuration.Services.GetBodyModelValidator())
{
}
//your code
}
Upvotes: 3