What is the correct way to download a file via the NSwag Code Generator (angular 2 typescript)

后端 未结 4 1778
盖世英雄少女心
盖世英雄少女心 2021-02-13 16:50

I try to download a file via an angular 2 typescript client. The link generated in Swagger UI works fine, but the generated typescript client does not.

The controller lo

相关标签:
4条回答
  • 2021-02-13 17:27

    The solution of @20B2 is working well, but instead of using

    () => new Schema
    

    You should use:

    () => new OpenApiSchema
    
    0 讨论(0)
  • 2021-02-13 17:50

    Found the response of this problem :

    In startup add :

    services.AddSwaggerGen(options =>
    {   
    options.MapType<FileContentResult>(() => new Schema
           {
                    Type = "file",
                });
    }
    

    And for your controller :

    [HttpPost()]
        [SwaggerResponse(200, typeof(FileContentResult))]
        [ProducesResponseType(typeof(FileContentResult), 200)]
        public async Task<FileResult> MyMethod(Viewmodel vm)
        {
    

    A late response but for people who has the same problem ...

    0 讨论(0)
  • 2021-02-13 17:52

    Eric Gontier's solution works great for Swashbuckle 4 and NSwag 12. If you've upgraded to swashbuckle 5 and thus OpenApi 3 and NSwag 13, then the solution is different. Instead you'll need a custom operation filter, and an reusable attribute to indicate the content-type result:

    Custom attribute

    /// <summary>
    /// Indicates swashbuckle should expose the result of the method as a file in open api (see https://swagger.io/docs/specification/describing-responses/)
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class FileResultContentTypeAttribute : Attribute
    {
        public FileResultContentTypeAttribute(string contentType)
        {
            ContentType = contentType;
        }
    
        /// <summary>
        /// Content type of the file e.g. image/png
        /// </summary>
        public string ContentType { get; }
    }
    

    Operation filter

    public class FileResultContentTypeOperationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            var requestAttribute = context.MethodInfo.GetCustomAttributes(typeof(FileResultContentTypeAttribute), false)
                .Cast<FileResultContentTypeAttribute>()
                .FirstOrDefault();
    
            if (requestAttribute == null) return;
    
            operation.Responses.Clear();
            operation.Responses.Add("200", new OpenApiResponse
            {
                Content = new Dictionary<string, OpenApiMediaType>
                {
                    {
                        requestAttribute.ContentType, new OpenApiMediaType
                        {
                            Schema = new OpenApiSchema
                            {
                                Type = "string",
                                Format = "binary"
                            }
                        }
                    }
                }
            });
        }
    }
    

    Startup.cs

    services.AddSwaggerGen(options =>
    {
        ...
        options.OperationFilter<FileResultContentTypeOperationFilter>();
    }
    

    Sample Controller

    Then annotate your controller with the attribute.

    [HttpPost]
    [Route("{fileName}.csv")]
    [FileResultContentType("text/csv")]
    public async Task<ActionResult> Generate(string fileName, [FromBody]MyDto myDto)
    {
        var fileMemoryStream = GetCsvAsBytes(myDto);
        return File(fileMemoryStream,
            "text/csv", fileName + ".csv");
    }
    
    0 讨论(0)
  • 2021-02-13 17:54

    In the API, Required Nuget packages:

    1. Microsoft.AspNetCore.StaticFiles // To determine MimeType
    2. NSwag.Annotations // To map the return type of API with Angular Service Generated by NSwag
    

    Search for the pacakges in Nuget and install them.

    Then In Startup.cs,

    services.AddSwaggerGen(options =>
    {
        // Swagger Configurations
        options.MapType<FileContentResult>(() => new Schema
        {
            Type = "file"
        });
    });
    

    Now add a method to get the MimeType of file

    private string GetMimeType(string fileName)
    {
        var provider = new FileExtensionContentTypeProvider();
        string contentType;
        if (!provider.TryGetContentType(fileName, out contentType))
        {
            contentType = "application/octet-stream";
        }
        return contentType;
    } 
    

    Now Add a method to download file

    [SwaggerResponse(200, typeof(FileContentResult))]
    [ProducesResponseType(typeof(FileContentResult), 200)]
    public FileContentResult DownloadDocument(string fileName)
    { 
        // _environment => IHostingEnvironment Instance
        var filepath = Path.Combine($"{this._environment.WebRootPath}\\path-to\\filename}");
    
        var mimeType = this.GetMimeType(filename);
    
        // Checks if file exists 
        var fileBytes = File.ReadAllBytes(filepath);
        return new FileContentResult(fileBytes, mimeType)
        {
            FileDownloadName = filename
        };
    }
    

    Now the downloadFile method in angular service generated by NSwag will return Observable. To Consume the service, first install file-saver using npm i file-saver. Then import it in component
    import { saveAs } from 'file-saver';

    downloadDocument = (filename: string): void => {
        this._service.downloadDocument(filename).subscribe((res) => {
          saveAs(res.data, 'filename');
        });
      };
    

    This will download file.

    0 讨论(0)
提交回复
热议问题