当前位置: 首页 > 知识库问答 >
问题:

返回文件的Web Api控制器的自动重发/切换生成代码

高朝明
2023-03-14

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

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

内容配置:附件;filename=Reporte。pdf属性长度:22331属性类型:应用程序/八位字节流

设置 swagger 后,在我的 Xamarin PCL 项目中生成 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中进行哪些更改,以正确生成应用程序/八位字节流内容响应的代码?

共有3个答案

吕嘉荣
2023-03-14

对于Swashbuckle版本4,我创建过滤器:

public class FileDownloadOperation : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        var rt = context.MethodInfo.ReturnType;
        if (rt == typeof(Stream) || 
            rt == typeof(Task<Stream>) || 
            rt == typeof(FileStreamResult) || 
            rt == typeof(Task<FileStreamResult>))
        {
            operation.Responses["200"] = new Response
            {
                Description = "Success", Schema = new Schema {Type = "file"}
            };
            operation.Produces.Clear();
            operation.Produces.Add("application/octet-stream");
        }
    }
}

将其分配到摇摆发电机中

services.AddSwaggerGen(c =>
            {
                ...
                c.OperationFilter<FileDownloadOperation>();
            });

然后只需使用简单的控制器:

[HttpGet("{fileId}")]
public async Task<FileStreamResult> GetMyFile(int fileId)
{
    var result = await _fileService.GetFile(fileId);
    return File(result.Stream, result.ContentType, result.FileName);
}
苗盛
2023-03-14

生成的代码将方法的输出视为 json,因为错误的类型写入了 swagger.json(可能是 .... #/definitions/....)。它应该包含“类型”:“文件”

您可以使用SwaggerGen选项来操作输出。

如果您的方法如下所示:

    [Produces("application/pdf")]
    [ProducesResponseType(200, Type = typeof(Stream))]
    public IActionResult Download()
    {           
        Stream yourFileStream = null; //get file contents here
        return new FileStreamResult(yourFileStream , new MediaTypeHeaderValue("application/pdf"))
        {
            FileDownloadName = filename
        };
    }

在您的启动中,设置Swagger生成,配置您要返回的Type与您要出现在Swagger文件中的Type之间的映射

     services.AddSwaggerGen(
            options =>
            {                   
                options.MapType<System.IO.Stream>(() => new Schema { Type = "file" });
            });

那么您生成的代码如下所示:

public async Task<HttpOperationResponse<System.IO.Stream>> DownloadWithHttpMessagesAsync()
雷献
2023-03-14

创建返回文件的自定义筛选器:

 [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>();

要使用此筛选器,只需将其添加到每个操作控制器中,如下所示:

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

因此,当swagger生成json元数据时,autorest将正确地创建一个返回任务的方法

 类似资料:
  • 我不知道该用什么。我有两页——简介。jsp(1)和booksList.jsp(2)。对于每个页面,我都创建了一个控制器类。第一页有打开第二页的按钮: 第一个问题是:我不确定这个按钮的正确性。它工作得很好,但是按下这个按钮后我有问号。 第二个问题是:当我按下该按钮时,调用带有下一个注释的方法(第二页的控制器): 我应该用这种方法返回什么?换句话说,我怎么能从第一页跳到第二页? < li> < li>

  • 我使用开放式api生成器(gradle的实现)为Java中的api生成控制器,但如果我的endpoint不返回任何内容,则OpenAPI生成器将返回类型生成为对象类型Void,而不是Void。 我希望: 但是得到了: 我试图自定义胡子模板,但没有任何选项。gradle任务配置中的ConfigOptions也没有相应的选项。 当前。小胡子api模板: 在gradle.build中生成任务示例 应用程

  • 我得到这个错误消息: 无法写入JSON: 未找到用于java.io.FileDescriptor类的序列化程序,也未发现用于创建BeanSerializer的属性 (为了避免异常,禁用SerializationFeature.fail_on_empty_beans)) (通过引用链: org.springframework.core.io.filesystemResource[\“outputSt

  • 生成一个 Http Controller 必选参数: -name 生成的 Controller 类名 -namespace 生成的 Controller 所在命名空间 可选参数: -prefix 路由前缀,不传则为类名 -render 渲染方式,默认为json,可选:html/json/xml -rest 是否生成 RESTful 风格,默认 false -override 是否覆盖已存在的文件,

  • 几个星期以来,我一直在想办法解决这个问题,现在我开始放弃了。我肯定做了什么根本错误的事情。不管怎样,这是我的情况。我以前用JavaFX做了一个简单的小程序,但现在发现了Scene Builder,并想用这个工具重新构建它。该项目包括几个经常重用的组件,如下面的组件。它由一个标签、一个滑块、一个文本字段和另一个自定义组件InfoIcon组成。 我已经了解到,在构建我称之为SliderVariable