Autorest/Swagger为返回文件的Web Api控制器生成的代码 [英] Autorest/Swagger generated code for Web Api controller that returns File
问题描述
在我的ASP.NET Web API应用程序中,我有一个像这样的控制器:
In my ASP.NET Web API application I have a controller like this:
[RoutePrefix("api/ratings")]
public class RateCostumerController : ApiController
{
[AllowAnonymous]
[Route("Report/GetReport")]
[HttpGet]
public HttpResponseMessage ExportReport([FromUri] string costumer)
{
var rd = new ReportDocument();
/*No relevant code here*/
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(ms.ToArray())
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = "Reporte.pdf"
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
}
因此,当我使用客户参数进行简单的GET请求时,会在浏览器中得到一个pdf文件作为响应.一些响应头:
So, when I make a simple GET request with a costumer parameter I get a pdf file in browser as response. Some of the response headers:
Content-Disposition:附件;filename = Reporte.pdf内容长度:22331内容类型:应用程序/八位字节流
Content-Disposition : attachment; filename=Reporte.pdf Content-Length : 22331 Content-Type : application/octet-stream
设置好摇摇欲坠之后,在我的Xamarin PCL项目中生成了json metadafile并由此生成了C#代码,我尝试使用该服务.但这失败了,因为在生成的代码中试图对JSON进行反序列化,但不是JSON结果!
After setting up swagger, generated json metadafile and generated C# code with it in my Xamarin PCL project I tried to consume the service. But it failed because in the generated code is trying to Deserialize json, but is not a json result!
这里是失败的生成代码的一部分:
Here it is part of the generated code where it fails:
[...]
var _result = new Microsoft.Rest.HttpOperationResponse<object>();
_result.Request = _httpRequest;
_result.Response = _httpResponse;
// Deserialize Response
if ((int)_statusCode == 200)
{
_responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
_result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject<object>(_responseContent, this.Client.DeserializationSettings);
}
catch (Newtonsoft.Json.JsonException ex)
{
_httpRequest.Dispose();
if (_httpResponse != null)
{
_httpResponse.Dispose();
}
throw new Microsoft.Rest.SerializationException("Unable to deserialize the response.", _responseContent, ex);
}
}
if (_shouldTrace)
{
Microsoft.Rest.ServiceClientTracing.Exit(_invocationId, _result);
}
return _result;
[...]
当我调试时,我发现文件的内容在主体中,因此反序列化将其弄乱了.由于不建议您编辑此生成的类文件,因此我需要在API中进行哪些更改才能正确生成用于应用程序/八位字节流内容响应的代码?
When I debugged I figure out that the content of the file is in the body, so deserialization is messing it up. Since is not recommended to edit this generated class file, What I need to change in my API to properly generate the code for application/octet-stream content-response?
推荐答案
创建返回文件的自定义过滤器:
Creating a custom filter that returns a file:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public sealed class SwaggerFileResponseAttribute : SwaggerResponseAttribute
{
public SwaggerFileResponseAttribute(HttpStatusCode statusCode) : base(statusCode)
{
}
public SwaggerFileResponseAttribute(HttpStatusCode statusCode, string description = null, Type type = null) : base(statusCode, description, type)
{
}
public SwaggerFileResponseAttribute(int statusCode) : base(statusCode)
{
}
public SwaggerFileResponseAttribute(int statusCode, string description = null, Type type = null) : base(statusCode, description, type)
{
}
}
还有这个自定义的ResponseTypeFilter类:
And also this custom ResponseTypeFilter class:
public sealed class UpdateFileResponseTypeFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (apiDescription.GetControllerAndActionAttributes<SwaggerResponseRemoveDefaultsAttribute>().Any())
{
operation.responses.Clear();
}
var responseAttributes = apiDescription.GetControllerAndActionAttributes<SwaggerFileResponseAttribute>()
.OrderBy(attr => attr.StatusCode);
foreach (var attr in responseAttributes)
{
var statusCode = attr.StatusCode.ToString();
Schema responseSchema = new Schema { format = "byte", type = "file" };
operation.produces.Clear();
operation.produces.Add("application/octet-stream");
operation.responses[statusCode] = new Response
{
description = attr.Description ?? InferDescriptionFrom(statusCode),
schema = responseSchema
};
}
}
private string InferDescriptionFrom(string statusCode)
{
HttpStatusCode enumValue;
if (Enum.TryParse(statusCode, true, out enumValue))
{
return enumValue.ToString();
}
return null;
}
}
然后将其注册到SwaggerConfig文件中:
Then register it in SwaggerConfig file:
c.OperationFilter<UpdateFileResponseTypeFilter>();
要使用此过滤器,只需将其添加到每个动作控制器中,如下所示:
To use this filter just add it in every action controller like this:
[Route("Report/GetReport/{folio}")]
[SwaggerFileResponse(HttpStatusCode.OK, "File Response")]
[HttpGet]
public HttpResponseMessage ExportReport(string folio)
{
...
因此,当swagger生成json元数据时,autorest将正确创建一个返回Task<的方法.Microsoft.Rest.HttpOperationResponse<System.IO.Stream>>
So, when swagger generates json metadata, autorest will properly create a method that returns a Task < Microsoft.Rest.HttpOperationResponse< System.IO.Stream > >
这篇关于Autorest/Swagger为返回文件的Web Api控制器生成的代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!