Angular/Web API:将FormFile信息从客户端发送到API

Angular/Web API:将FormFile信息从客户端发送到API,angular,asp.net-web-api,.net-core,asp.net-core-webapi,Angular,Asp.net Web Api,.net Core,Asp.net Core Webapi,我正在努力在Angular应用程序中创建一个包含0-n个子对象(文件“附件”)的对象(“票证”),并将信息发送到我的dotnet core Web Api。但这是一个更符合逻辑的问题 当用户创建票据时,他应该能够向该票据添加附件 关于如何上传文件的文章很多,但我不想看到的是,用户在创建票据时上传附件。我不想这样做,因为如果用户取消票证创建,我的存储(Azure)中将有未使用的附件,如果没有对entity framework服务器端的票证TID(这次不存在)的引用,我将无法创建附件对象 所以我喜欢

我正在努力在Angular应用程序中创建一个包含0-n个子对象(文件“附件”)的对象(“票证”),并将信息发送到我的dotnet core Web Api。但这是一个更符合逻辑的问题

当用户创建票据时,他应该能够向该票据添加附件

关于如何上传文件的文章很多,但我不想看到的是,用户在创建票据时上传附件。我不想这样做,因为如果用户取消票证创建,我的存储(Azure)中将有未使用的附件,如果没有对entity framework服务器端的票证TID(这次不存在)的引用,我将无法创建附件对象

所以我喜欢先收集所有信息,如果用户说“创建票证”,所有信息都会发送到API

  • 将创建票证对象
  • 文件将根据文件信息上载(到Azure)
  • 将创建附件对象
  • 附件对象已添加到票证中,并且
  • 所有内容都保存到数据库中
  • 角度(客户端)

    表单以收集票证数据并为附件选择文件。界面如下所示:

    export interface ITicketCreate {
        subject: string;
        content: string;
        category: number;
        userId: string;
        userNickname: string;
        email: string;
        attachments: File[];
    }
    
    发送到Api的最终Dto对象如下所示:

    export interface ITicketCreate {
        subject: string;
        content: string;
        category: number;
        userId: string;
        userNickname: string;
        email: string;
        attachments: File[];
    }
    

    具有createTicket功能的服务

    export class SupportService {
      supportUrl = environment.SupportApiUrl;
    
    constructor(private http: HttpClient) { }
    
      createTicket(model: ITicketCreate) {
        return this.http.post(this.supportUrl + 'tickets', model);
      }
    ...
    }
    
    以及CreateTicketComponent中的调用

    ticket = {} as ITicketCreate;
    ...
    ngOnInit() {
        this.ticket.attachments = [];
    }
    
    createTicket() {
        console.log(this.ticket);
    
        this.ticket.userId = this.authService.currentUser.id;
        this.ticket.userNickname = this.authService.currentUser.nickName;
        this.ticket.email = this.authService.currentUser.userName;
    
        this.supportService.createTicket(this.ticket).subscribe(next => {
          this.alertify.success('Ticket created');
          this.router.navigate(['/support/tickets']);
        }, error => {
          this.alertify.error(error);
          console.log(error);
        });
      }
    
    Web Api(.Net Core 2.2)

    在服务器端,我有以下Dto来接收数据:

    public class CreateTicketDto
    {
        public string Subject { get; set; }
        public string Content { get; set; }
        public string UserId { get; set; }
        public string Email { get; set; }
        public string UserNickname { get; set; }
        public int Category { get; set; }
    
        public List<IFormFile> Attachments { get; set; } = new List<IFormFile>();
    }
    
    public类CreateTicketDto
    {
    公共字符串主题{get;set;}
    公共字符串内容{get;set;}
    公共字符串用户标识{get;set;}
    公共字符串电子邮件{get;set;}
    公共字符串用户昵称{get;set;}
    公共int类{get;set;}
    公共列表附件{get;set;}=new List();
    }
    
    以及控制器的动作

    [HttpPost]
    public async Task<IActionResult> CreateTicket(CreateTicketDto createTicket)
    
    [HttpPost]
    公共异步任务CreateTicket(CreateTicket到CreateTicket)
    
    发送请求后,我从API中得到的错误是:

    由于我不是为了调试而输入控制器操作本身,我的问题是:

  • 这可能是我喜欢的工作方式吗
  • 如果是,我遗漏了什么
  • 致以最良好的祝愿,
    Kay

    实际的错误消息告诉您无法实例化接口/抽象类。这是正常的,因为不能实例化接口或抽象类,所以需要非抽象类来实例化它。因此,您需要检查这一行

    public List<IFormFile> Attachments { get; set; } = new List<IFormFile>();
    
    公共列表附件{get;set;}=new List();
    
    并查看是否可以通过使用IFormFile接口的完整实现类来消除障碍,以便在尝试添加附件时不会出现此错误

    至于你的实际问题,你有几个选择:

    不允许在插入时上载附件 这是一个简单的想法。在“添加”模式下,您不显示任何附件,也不允许上载文件,但在“编辑”模式下,您允许上载文件。您可以显示文件上载部分的禁用版本,并确保在服务器端不存储任何尝试的上载

    有两个单独的表单用于票据上传和文件上传 在这种情况下,您将显示所有情况下的所有内容,甚至允许准备上载文件,但仅在实际创建了票证时才允许上载附件

    使用proto类获取票证
    您可以为票证实现一个proto类,因此,如果票证尚未创建,但发送了附件,则将使用创建它的用户的外键创建一个ProtoTicket,因此您将跟踪附件,并允许用户在实际创建票证之前上载附件。下次用户创建票证时,他/她与proto票证的所有附件都将与该新票证关联,并且proto票证将被删除。你也可以定期清理原始票。我认为这是最好的解决方案,因为它提供了最好的UX并保护您的服务器免受垃圾文件的侵害。新年快乐!