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

日期参数为空致API报错:非可空System.DateTime类型Fromdate参数缺失

请求无效错误排查:Fromdate参数空值问题

错误信息

{"Message":"请求无效","MessageDetail":"参数字典中,'shenoyapi.Controllers.ReportController'内的CustomerWiseQuotationHistory方法的非可空类型System.DateTime参数'Fromdate'存在空条目。可选参数必须是引用类型、可空类型或声明为可选参数。"}

相关代码

Component.ts 验证逻辑

validate(): boolean { 
  this.appErrors = []; 
  if (this.objProductQuotationHistory.ProductName=='') { 
    this.appErrors.push({ Title: 'Select Product Name.' }); 
  } 
  if (this.objProductQuotationHistory.FromDate > this.objProductQuotationHistory.ToDate) { 
    this.appErrors.push({ Title: 'From Date date cannot be greater than To Date.' }); 
  } 
  if (this.appErrors.length > 0) { 
    return false; 
  } else { 
    return true; 
  } 
}

print(type: string ): void { 
  if (!this.validate()) { 
    const dialogRef = this.dialog.open(AlertdialogComponent, { data: this.appErrors }); 
    return; 
  }
  // 此处应包含调用getProductHistory的逻辑
}

Services.ts 请求方法

getProductHistory(productId: number,FromDate: Date,ToDate:Date): Observable<number>{ 
  let f = moment(FromDate); 
  let fromdate = f.format('YYYY-MM-DD'); 
  let t = moment(ToDate); 
  let todate = t.format('YYYY-MM-DD'); 
  return this.http.get<number>(AppConfig.BASE_API_URL +'/api/report/productwisequotationhistoryreport/' + productId + '/' + fromdate + '/' + todate); 
}

API 接口代码(C#)

[Route("api/report/productwisequotationhistoryreport/{id}/{type}/{Fromdate}/{ToDate}")]
[HttpGet]
public HttpResponseMessage ProductrWiseQuotationHistory(long id, string type, DateTime Fromdate, DateTime ToDate) { 
  try { 
    rptViewer.ProcessingMode = ProcessingMode.Local; 
    rptViewer.LocalReport.ReportEmbeddedResource = "shenoyapi.Reports.ProductWiseQuotationHistoryReport.rdlc"; 
    var dsHeaderData = SqlHelper.ExecuteDataset(ConfigurationManager.ConnectionStrings["AppDatabaseConnection"].ToString(), CommandType.StoredProcedure, 0, "GetReportHeader"); 
    var dtHeader = new ProductWiseQuotationHistoryDataSource.GetReportHeaderDataTable(); 
    foreach (DataRow item in dsHeaderData.Tables[0].Rows) { 
      dtHeader.AddGetReportHeaderRow( 
        Convert.ToInt64(item[0]), item[1].ToString(), item[2].ToString(), item[3].ToString(), item[4].ToString(), item[5].ToString(), item[6].ToString(), item[7].ToString(), item[8].ToString() 
      ); 
    } 
    var dsHeader = new ReportDataSource("dsHeader", (DataTable)dtHeader); 
    rptViewer.LocalReport.DataSources.Add(dsHeader); 

    var dsProductWiseQuotationHistoryData = SqlHelper.ExecuteDataset(ConfigurationManager.ConnectionStrings["AppDatabaseConnection"].ToString(), CommandType.StoredProcedure, 0, "GetProductWiseQuotationHistory", new SqlParameter("@ProductId", id), new SqlParameter("@Fromdate", Fromdate), new SqlParameter("@ToDate", ToDate)); 
    var dtProductWiseQuotationHistoryHeader = new ProductWiseQuotationHistoryDataSource.GetProductWiseQuotationHistoryDataTable(); 
    foreach (DataRow item in dsProductWiseQuotationHistoryData.Tables[0].Rows) { 
      dtProductWiseQuotationHistoryHeader.AddGetProductWiseQuotationHistoryRow( 
        Convert.ToInt64(item[0]), Convert.ToDateTime(item[1]), Convert.ToInt32(item[2]), Convert.ToInt32(item[3]), item[4].ToString(), Convert.ToDateTime(item[5]), item[6].ToString(), item[7].ToString(), Convert.ToDecimal(item[8]), Convert.ToDecimal(item[9]), Convert.ToDecimal(item[10]), Convert.ToDecimal(item[11]), Convert.ToDecimal(item[12]), item[13].ToString(), item[14].ToString(), item[15].ToString(), item[16].ToString(), item[17].ToString(), item[18].ToString(), item[19].ToString(), item[20].ToString(), item[21].ToString(), item[22].ToString(), item[23].ToString(), item[24].ToString(), item[25].ToString(), item[26].ToString(), item[27].ToString(), Convert.ToDecimal(item[28]), Convert.ToDecimal(item[29]), Convert.ToDecimal(item[30]), Convert.ToInt32(item[31]), item[32].ToString(), item[33].ToString(), item[34].ToString(), item[35].ToString(), item[36].ToString(), item[37].ToString(), (byte[])item[38], item[39].ToString(), Convert.ToDecimal(item[40]), Convert.ToDecimal(item[41]), Convert.ToDecimal(item[42]) 
      ); 
    } 
    var dsProductWiseQuotationHistoryHeader = new ReportDataSource("dsReport", (DataTable)dtProductWiseQuotationHistoryHeader); 
    rptViewer.LocalReport.DataSources.Add(dsProductWiseQuotationHistoryHeader); 

    byte[] fileBytes = new byte[0]; 
    if (type == "Excel") { 
      fileBytes = rptViewer.LocalReport.Render("Excel", null, out mimeType, out encoding, out extension, out streamIds, out warnings); 
    } else if (type == "PDF") { 
      fileBytes = rptViewer.LocalReport.Render("PDF", null, out mimeType, out encoding, out extension, out streamIds, out warnings); 
    } 

    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK); 
    response.Content = new ByteArrayContent(fileBytes); 
    response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); 
    if (type == "Excel") { 
      response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xls"); 
      response.Content.Headers.ContentDisposition.FileName = "QuotationReport_" + DateTime.Now.Ticks.ToString() + ".xls"; 
    } else if (type == "PDF") { 
      response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); 
      response.Content.Headers.ContentDisposition.FileName = "QuotationReport_" + DateTime.Now.Ticks.ToString() + ".pdf"; 
    } 
    return response; 
  } catch (Exception ee) { 
    throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotAcceptable, ee.Message)); 
  }
}

问题分析

我仔细梳理了你的代码,发现几个核心问题:

  1. API路由与请求URL不匹配:API路由定义是{id}/{type}/{Fromdate}/{ToDate},但前端请求URL只拼接了productId + '/' + fromdate + '/' + todate,完全漏掉了type参数!这导致后端接收的参数顺序完全错乱:原本type的位置被fromdate占据,Fromdate的位置被todate占据,最后ToDate的位置没有值,直接触发非可空参数空值错误。
  2. 前端验证遗漏:当前validate()方法只验证了ProductName和日期大小,没有检查FromDate和ToDate是否为空,如果用户未选择日期,空值传到后端就会报错。
  3. 方法名拼写/混淆问题:错误信息提到的是CustomerWiseQuotationHistory方法,但你的API代码里是ProductrWiseQuotationHistory(注意多了一个r),要么是代码粘贴错误,要么是项目中存在同名方法导致混淆。

解决方案

按优先级依次处理:

1. 修复请求URL与路由的匹配问题

修改Services.ts的getProductHistory方法,补充type参数并按路由顺序拼接URL:

getProductHistory(productId: number, type: string, FromDate: Date, ToDate:Date): Observable<number>{ 
  let f = moment(FromDate); 
  let fromdate = f.format('YYYY-MM-DD'); 
  let t = moment(ToDate); 
  let todate = t.format('YYYY-MM-DD'); 
  // 按路由顺序拼接参数:id/type/fromdate/todate
  return this.http.get<number>(`${AppConfig.BASE_API_URL}/api/report/productwisequotationhistoryreport/${productId}/${type}/${fromdate}/${todate}`); 
}

同时在Component.ts的print方法中调用时传入type参数:

print(type: string ): void { 
  if (!this.validate()) { 
    const dialogRef = this.dialog.open(AlertdialogComponent, { data: this.appErrors }); 
    return; 
  }
  // 调用时传入type参数
  this.yourServiceName.getProductHistory(this.objProductQuotationHistory.ProductId, type, this.objProductQuotationHistory.FromDate, this.objProductQuotationHistory.ToDate)
    .subscribe(res => { /* 后续处理逻辑 */ });
}

2. 完善前端验证逻辑

在validate()方法中添加日期非空验证:

validate(): boolean { 
  this.appErrors = []; 
  if (!this.objProductQuotationHistory.ProductName) { 
    this.appErrors.push({ Title: 'Select Product Name.' }); 
  } 
  // 验证FromDate非空
  if (!this.objProductQuotationHistory.FromDate) { 
    this.appErrors.push({ Title: 'Please select From Date.' }); 
  } 
  // 验证ToDate非空
  if (!this.objProductQuotationHistory.ToDate) { 
    this.appErrors.push({ Title: 'Please select To Date.' }); 
  } 
  // 先判断日期存在再比较大小,避免空值报错
  if (this.objProductQuotationHistory.FromDate && this.objProductQuotationHistory.ToDate && this.objProductQuotationHistory.FromDate > this.objProductQuotationHistory.ToDate) { 
    this.appErrors.push({ Title: 'From Date cannot be greater than To Date.' }); 
  } 
  return this.appErrors.length === 0; 
}

3. 修正API方法名拼写错误

将API中的ProductrWiseQuotationHistory改为ProductWiseQuotationHistory(去掉多余的r),同时确认项目中是否存在CustomerWiseQuotationHistory方法,避免混淆。

4. 后端参数容错处理(可选)

为了防止前端验证失效,可将API的DateTime参数改为可空类型,内部做非空判断:

public HttpResponseMessage ProductWiseQuotationHistory(long id, string type, DateTime? Fromdate, DateTime? ToDate) { 
  // 先校验参数非空
  if (!Fromdate.HasValue || !ToDate.HasValue) {
    return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "From Date and To Date are required.");
  }
  // 后续逻辑使用Fromdate.Value和ToDate.Value
  ...
}

完成以上修改后,应该就能解决这个请求无效的问题了。


内容的提问来源于stack exchange,提问作者Keep Watching Madani Channel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:23:16