Skip to main content
 首页 » 编程设计

c#之Autorest/Swagger 为返回文件的 Web Api Controller 生成代码

2024年01月22日45jirigala

在我的 ASP.NET Web API 应用程序中,我有一个这样的 Controller :

    [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 文件作为响应。一些响应 header :

内容处理:附件; filename=Reporte.pdf 内容长度:22331 内容类型:应用程序/八位字节流

在我的 Xamarin PCL 项目中设置 swagger、生成 json 元数据文件并生成 C# 代码后,我尝试使用该服务。 但它失败了,因为在生成的代码中试图反序列化 json,但不是 json 结果!

这是生成的代码失败的部分:

[...] 
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 中更改什么才能正确生成应用程序/八位字节流内容响应的代码?

请您参考如下方法:

创建返回文件的自定义过滤器:

 [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 类:

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文件中注册:

c.OperationFilter<UpdateFileResponseTypeFilter>(); 

要使用这个过滤器,只需像这样在每个 Action Controller 中添加它:

 [Route("Report/GetReport/{folio}")] 
        [SwaggerFileResponse(HttpStatusCode.OK, "File Response")] 
        [HttpGet] 
        public HttpResponseMessage ExportReport(string folio) 
        { 
... 

因此,当 swagger 生成 json 元数据时,autorest 将正确创建一个返回任务的方法 < Microsoft.Rest.HttpOperationResponse< System.IO.Stream >>