Closed MaxDeg closed 7 years ago
The type file is nit valid in json schema - it is only allowed in swagger. How is a stream property serialized in json.net? Is it a byte array encoded as base64 (same as byte[])?
I don't know how it's serialized. The issue I'm facing is that I have on controller in aspnet core that take a Stream as parameter (from body). But Streamis not recognized as a "File" type by swagger. My search finished here: https://github.com/RSuter/NJsonSchema/blob/13ff54b32fe8cd4b9ef4f50ae4f0d6b10b7c4099/src/NJsonSchema/Generation/JsonObjectTypeDescription.cs#L203. Is it possible to handle Stream type in addition of IFormFile or HttpPostedFile?
I hope I'm clear enough :)
Yes. Is a stream body parameter encoded as formData? Same as HttpPostedFile?
In my case it's a raw binary content without formData nor multipart. And I assume it always be the case. Otherwise you could use IFormFile or HttpPostedFile type from aspnet.
I think we have to add this special case here:
https://github.com/RSuter/NSwag/blob/master/src/NSwag.SwaggerGeneration/SwaggerGenerator.cs#L150
The question is: How is this correctly described in a Swagger spec?
As seen in https://github.com/swagger-api/swagger-codegen/issues/669
{
"name": "BinaryData",
"in": "body",
"required": true,
"schema": {
"type": "string",
"format": "byte"
}
}
But this will not expect a binary body but a JSON string which is base64 encoded like
"ABC="
Maybe we just use body and type file:
{
"name": "BinaryData",
"in": "body",
"required": true,
"schema": {
"type": "file"
}
}
and handle this correctly in the code generators...?
Or maybe its better to use
"schema": {
"type": "string",
"format": "byte"
}
and consumes "application/octet-stream" as described in https://github.com/OAI/OpenAPI-Specification/issues/50
"consumes": [
"application/octet-stream"
]
There is a lot of change for that in the 3.0 spec but for the 2.0:
Form - Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation). This is the only parameter type that can be used to send files, thus supporting the file type.
So I assume body and type file is not correct. Indeed type: string and format: byte should be a better idea. But in that case it should be mapped to a Stream (that support byte[] and it allow streaming of request which is interesting in the case of big file)
Or even better
"schema": {
"type": "string",
"format": "binary"
}
Per swagger 2.0 spec
binary | string | binary | any sequence of octets https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
and keeping
"consumes": [
"application/octet-stream"
]
"binary" is not an official format string in JSON Schema, I think we should go with "byte" and "application/octet-stream"
byte is supposed to be "base64 encoded characters". But I can definitively live with it :)
I see it like that:
If "consumes" is "application/json" it is "base64 encoded characters" If "consumes" is "application/octet-stream" it is processed as binary
don't you think this is how it works?
Ok I have this operation:
[HttpPost, Route("upload")]
public async Task<byte[]> Upload([FromBody] Stream data)
{
using (var ms = new MemoryStream())
{
data.CopyTo(ms);
return ms.ToArray();
}
}
but this gives me the following exception:
{"Message":"The request entity's media type 'application/octet-stream' is not supported for this resource.","ExceptionMessage":"No MediaTypeFormatter is available to read an object of type 'Stream' from content with media type 'application/octet-stream'.","ExceptionType":"System.Net.Http.UnsupportedMediaTypeException","StackTrace":" at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)\r\n at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)"}
How is this working?
This is the client
var content_ = new System.Net.Http.StreamContent(data);
content_.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
request_.Content = content_;
request_.Method = new System.Net.Http.HttpMethod("POST");
request_.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
I created a custom InputFormatter (https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-formatters#how-to-create-a-custom-formatter-class) to bind Stream type. It's more testable than reading directly the stream from the Request object. An example https://stackoverflow.com/questions/41346128/model-binding-not-working-with-stream-type-parameter-in-asp-net-core-webapi-cont
Can you please provide a simple handler (ideally applicable with a single attribute) so that we can add it to the ASP.NET (Core) package and add documentation for this feature?
What do you mean by an handler?
A custom formatter which can be applied to an operation method with an attribute
The only way to add a inputformatter is by adding it in the startup class on the MvcOptions object. It was doable before using a IResourceFilter but since https://github.com/aspnet/Mvc/issues/4290 it has been removed.
I will take a look for ModelBinding if we could do it there.
Is there any reason why Stream type fro System.IO is not considered as a File type in json? If no I could make a PR to resolve this.
Thanks