通过 NSwag 代码生成器(angular 2 typescript)下载文件的正确方法是什么

问题描述 投票:0回答:4

我尝试通过 angular 2 打字稿客户端下载文件。在 Swagger UI 中生成的链接工作正常,但生成的打字稿客户端没有。

控制器看起来像这样:

    [HttpGet("export")]
    [SwaggerResponse((int) HttpStatusCode.OK, Type = typeof(FileContentResult))]
    [ProducesResponseType(typeof(FileResult), (int) HttpStatusCode.OK)]
    [Produces("text/csv")]
    public virtual FileResult Export(int Id, string fileType, CsvFormat format, bool includeHeader)
    {
        .
        .
        .
        FileStreamResult file = new FileStreamResult(s, "text/csv");
        file.FileDownloadName = ts.Name + "." + fileType;

        return file;
    }

招摇用户界面:

生成的 typescript 客户端如下所示。如您所见,已设置 responseText 但从未返回。我错过了什么?

protected processRestTimeSeriesExportGet(response: Response): Observable<void> {
    const status = response.status; 

    if (status === 200) {
        const responseText = response.text();
        return Observable.of<void>(<any>null);
    } else if (status !== 200 && status !== 204) {
        const responseText = response.text();
        return throwException("An unexpected server error occurred.", status, responseText);
    }
    return Observable.of<void>(<any>null);
}

致以诚挚的问候

typescript .net-core swagger-ui nswag
4个回答
26
投票

Eric Gontier 的解决方案非常适用于 Swashbuckle 4 和 NSwag 12。如果您已经升级到 swashbuckle 5 并因此升级到 OpenApi 3 和 NSwag 13,那么解决方案就不同了。相反,您需要一个自定义操作过滤器和一个可重用的属性来指示内容类型结果:

自定义属性

/// <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; }
}

运算过滤器

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

样品控制器

然后用属性注释你的控制器。

[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");
}

9
投票

找到这个问题的答案:

启动时添加:

services.AddSwaggerGen(options =>
{   
    options.MapType<FileResult>(() =>
    {
        return new Microsoft.OpenApi.Models.OpenApiSchema
        {
            Type = "string",
            Format = "binary",
        };
    });
}

对于您的控制器:

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

或者以 Minimal API 风格

app.MapGet("/download", () => { ... }).Produces<FileStreamResult>()

迟到的回复,但对于有同样问题的人......


3
投票

在API中, 所需的 Nuget 包:

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

在 Nuget 中搜索包并安装它们。

然后在 Startup.cs 中,

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

现在添加一个获取文件MimeType的方法

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

现在添加下载文件的方法

[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
    };
}

现在 NSwag 生成的角度服务中的 downloadFile 方法将返回 Observable。要使用该服务,请先使用

npm i file-saver
安装文件保护程序。然后在组件中导入它
import { saveAs } from 'file-saver';

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

这将下载文件。


0
投票

@20B2 的解决方案运行良好,但不是使用

() => new Schema

你应该使用:

() => new OpenApiSchema
© www.soinside.com 2019 - 2024. All rights reserved.