You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 12文件上传至.NET Core后端时File字段为空求助

文件上传后端接收File为null问题排查方案

问题描述

Angular 12实现文件+数据上传功能,前端表单数据检查正常,但.NET Core服务端接收时File字段为null,DTO与前端变量名称一致,附上前后端代码请求排查。

后端代码

DTO类

public class SampleAnalysisFileDto
{
   // 缺少public修饰符,默认私有,模型绑定无法读取
   IFormFile File { get; set; }
   public string Name { get; set; }
}

控制器方法

[HttpPost("{id}")]
[GrandAuthorize(GrandPermission.EndpointPermission, Grands.SampleAnalysisUpdate + "," + Grands.SampleAnalysesByPerosonnel, GrandFilterType.Or)]
public async Task<ServiceResponse<bool>> SampleAnalysisUpdate(int id, [FromForm] SampleAnalysisFileDto model)
{
    return await AspectProxy<IOrderService>.Create(_orderService).SampleAnalysisUpdate(id, model);
}

前端代码

基础服务方法

update(apiControllerName: string, metot: string, entity: any) {
    // 后端用HttpPost,此处用put方法,请求方法不匹配
    return this.http.put<any>(environment.apiUrl + '/' + apiControllerName + '/' + metot + '/' + 1, entity, { headers: this.jwt_header() });
}

组件提交逻辑

onFile(id: number) {
    this._genelServis.onFile(id);
}

onSubmit() {
    const formData: FormData = new FormData();
    formData.append('file', this.file_);
    formData.append('name','deneme');
    
    this._genelServis.lodingShow();
    this._baseServis.update("Order", "SampleAnalysisUpdate", formData).subscribe(
        (data: any) => {
            this._genelServis.lodinghide();
            if (Helpers.ExcuteData(data, false) === true) {
                this.onSubmitSaveAfter.emit(this.entity.id);
                this.modalClose();
            }
        },
        error => {
            this._genelServis.lodinghide();
        });
}

组件HTML

<div class="modal-header">
    <h4 class="modal-title"><app-language-com [labelCode]="'UI_ANALIZI_RAPOR'"></app-language-com></h4>
    <button type="button" class="close" aria-label="Close" (click)="activeModal.close('Close click')">
        <span aria-hidden="true">&times;</span>
    </button>
</div>

<div fxLayout="row" fxLayoutWrap="wrap">
    <!-- column -->
    <div fxFlex.gt-sm="100" fxFlex.gt-xs="100" fxFlex="100">
        <mat-card>
            <mat-card-content>
                <!-- Row -->
                <form class="basic-form" (ngSubmit)="onSubmit()" autocomplete="off" enctype="multipart/form-data">
                    <div fxLayout="row wrap" class="vys-form-element">
                        <!-- column -->
                        <div fxFlex.gt-md="100" fxFlex="100" class="vys-form-element" style="margin-top: 10px;">
                            <mat-form-field>
                                <mat-label>* <app-language-com [labelCode]="'UI_RAPOR_ACIKLAMA'"></app-language-com></mat-label>
                                <textarea matInput type="text" value="" name="input1reportContent" [(ngModel)]="entity.reportContent" style="height: 105px;" maxlength="2000"></textarea>
                            </mat-form-field>
                            <app-knumber-com [entity]="entity?.statusContent" [knumber]="2000"></app-knumber-com>
                        </div>
                        <div fxFlex.gt-sm="100" fxFlex="100" class="vys-form-element">
                            <input type="file" id="files" style="display: contents;" (change)="onfileSelected($event)" />

                            <a href="javascript:;" title="Rapor Seç">
                                <mat-icon role="img" for="files" class="mat-icon notranslate material-icons mat-icon-no-color" aria-hidden="true" data-mat-icon-type="font">file_download</mat-icon>
                                <label for="files">Rapor Seç</label>
                            </a>
                            &nbsp;&nbsp;<label>{{fileName}}</label>&nbsp;
                            <a href="javascript:;" title="Dosyayı Sil" (click)="entity.reportFileId = 0;fileName = '';" *ngIf="entity.reportFileId > 0">
                                <mat-icon role="img" for="files" class="mat-icon notranslate material-icons mat-icon-no-color" aria-hidden="true" data-mat-icon-type="font">delete</mat-icon>
                                &nbsp;<label>Dosyayı Sil</label>
                            </a>
                        </div>
                        <div fxFlex.gt-sm="70" fxFlex="70" style="text-align: right;margin-top:10px">
                            <button mat-raised-button color="primary" type="submit"><app-language-com [labelCode]="'UI_KAYDET'"></app-language-com></button>    &nbsp;&nbsp;
                            <button type="button" class="btn btn-light" id="modalUsercloseId" (click)="activeModal.close('Close click')"><app-language-com [labelCode]="'UI_KAPAT'"></app-language-com></button>
                        </div>
                    </div>
                </form>
            </mat-card-content>
        </mat-card>
    </div>
    <!-- column -->
</div>

核心问题与修复方案

1. HTTP请求方法不匹配

后端控制器标注[HttpPost],但前端用http.put发送请求,导致后端无法正确接收FormData。
修复:
新增post方法替换update方法:

post(apiControllerName: string, metot: string, id: number, entity: any) {
    return this.http.post<any>(`${environment.apiUrl}/${apiControllerName}/${metot}/${id}`, entity, { headers: this.jwt_header() });
}

// 组件调用替换为
this._baseServis.post("Order", "SampleAnalysisUpdate", id, formData).subscribe(...)

2. DTO属性访问修饰符缺失

SampleAnalysisFileDto中的File属性无public修饰符,默认私有,模型绑定无法读取。
修复:
添加public修饰符:

public class SampleAnalysisFileDto
{
   public IFormFile File { get; set; }
   public string Name { get; set; }
}

3. 请求头冲突(潜在问题)

发送FormData时Angular会自动设置Content-Type: multipart/form-data,若jwt_header()手动设置了application/json,会导致后端解析失败。
修复:
发送FormData时移除自定义Content-Type:

post(apiControllerName: string, metot: string, id: number, entity: any) {
    let headers = this.jwt_header();
    if (entity instanceof FormData) {
        headers.delete('Content-Type');
    }
    return this.http.post<any>(`${environment.apiUrl}/${apiControllerName}/${metot}/${id}`, entity, { headers });
}

4. 统一FormData键名(可选)

前端append的file与后端DTO的File大小写不一致,虽.NET默认不区分,但统一可避免潜在问题:
修复:

formData.append('File', this.file_);

5. 确认文件对象正确赋值

检查onfileSelected方法是否正确赋值文件:

onfileSelected(event: any) {
    if (event.target.files.length > 0) {
        this.file_ = event.target.files[0];
        this.fileName = this.file_.name;
    }
}

内容的提问来源于stack exchange,提问作者murato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 08:40:31