我有一個帶有控制器/服務/存盤庫結構的 C# WebAPI 專案。我的大多數控制器方法呼叫一個服務方法,然后呼叫一個存盤庫方法來訪問資料庫。如果控制器方法的引數之一為空,則ArgumentNullException拋出一個從我的 HttpGlobalExceptionFilter.cs 回傳以下內容:
{
"code": 400,
"messages": [
"Value cannot be null. (Parameter 'year')"
],
"developerMessage": null
}
但是,在控制器級別拋出的例外呢?例如,這是我的控制器方法:
public async Task<ActionResult<Data>> GetDataByYearAsync([FromRoute] string DataId)
{
int dId;
DataType dType;
try
{
string[] dataParts = DataId.Split('-');
dType = (DataType)Enum.Parse(typeof(DataType), dataParts[0]);
dId = int.Parse(dataParts[1]);
}
catch (Exception ex)
{
return BadRequest();
}
Data result = await _dataService.FindData(dType, dId).ConfigureAwait(false);
if (result == null)
return NotFound();
return result;
}
如果DataId引數是null那么我得到以下例外:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
在這一行拋出:
string[] dataParts = DataId.Split('-');
這導致以下情況:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Bad Request",
"status": 400,
"traceId": "00-a0aaaa0aa000000aa00a00a00000a0a0-a0a000000000a000-00"
}
我應該如何在控制器級別回傳錯誤/例外?
uj5u.com熱心網友回復:
您可以使用錯誤的請求拋出例外
回傳 BadRequest(ex.Message)
uj5u.com熱心網友回復:
似乎不同之處在于您從資料庫獲取資料的嘗試未包含在 try/catch 塊中,因此任何例外都會直接進入全域處理程式。但是string[] dataParts = DataId.Split('-');正在通過回傳 BadRequest 來捕獲和處理 NullReferenceException。
因此,您可以洗掉 try/catch 并允許全域處理任何例外:
public async Task<ActionResult<Data>> GetDataByYearAsync([FromRoute] string DataId)
{
int dId;
DataType dType;
string[] dataParts = DataId.Split('-');
dType = (DataType)Enum.Parse(typeof(DataType), dataParts[0]);
dId = int.Parse(dataParts[1]);
Data result = await _dataService.FindData(dType, dId).ConfigureAwait(false);
if (result == null)
return NotFound();
return result;
}
或者您將訊息包含在回傳的 BadRequest 中:
public async Task<ActionResult<Data>> GetDataByYearAsync([FromRoute] string DataId)
{
int dId;
DataType dType;
try
{
string[] dataParts = DataId.Split('-');
dType = (DataType)Enum.Parse(typeof(DataType), dataParts[0]);
dId = int.Parse(dataParts[1]);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
Data result = await _dataService.FindData(dType, dId).ConfigureAwait(false);
if (result == null)
return NotFound();
return result;
}
您也可以通過在控制器中處理資料錯誤來進行一些清理:
public async Task<ActionResult<Data>> GetDataByYearAsync([FromRoute] string DataId)
{
try
{
string[] dataParts = dataId.Split('-');
DataType dType = (DataType)Enum.Parse(typeof(DataType), dataParts[0]);
int dId = int.Parse(dataParts[1]);
Data result = await _dataService.FindData(dType, dId).ConfigureAwait(false);
if (result == null)
return NotFound();
return result;
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
但是,您在決議傳遞的眾所周知的字串時也會遇到各種潛在問題,因此您可以通過確保正確檢查輸入來減少潛在的例外:
注意:方法引數通常應該是 camelCase。
public async Task<ActionResult<Data>> GetDataByYearAsync([FromRoute] string dataId)
{
// check whether the argument is null before trying to use it.
if (string.IsNullOrEmpty(dataId))
return BadRequest($"Value cannot be null: {nameof(dataId)}");
string[] dataParts = DataId.Split('-');
// ensure that the string is in the expected format before progressing.
if (dataParts.Length != 2)
return BadRequest($"{nameof(dataId)} is invalid");
// Use Enum.TryParse to ensure no exception is thrown for a non-matching string.
if (!Enum.TryParse(dataParts[0], out DataType dType))
return BadRequest($"{nameof(dataId)} unknown DataType");
// Use int.TryParse to elegantly return if the value is invalid.
if (!int.TryParse(dataParts[1], out int dId))
return BadRequest($"{nameof(dataId)} could not parse integer value");
// this will still throw and be handled globally if there are any issues.
Data result = await _dataService.FindData(dType, dId).ConfigureAwait(false);
if (result == null)
return NotFound();
return result;
}
uj5u.com熱心網友回復:
假設您使用的是 ASP.NET 6,在 ASP.NET 6 中必須要求不可為空的屬性,否則模型驗證將失敗。
只需使用以下命令使您的引數可以為空?:
public async Task<ActionResult<Data>> GetDataByYearAsync([FromRoute] string? DataId)
或者另一種方式,<Nullable>enable</Nullable>從您的專案檔案中洗掉以全域忽略Required驗證。
如果您的引數包含模型資料并且在此模型中包含其他模型驗證,您可以在執行操作之前洗掉[ApiController]以跳過驗證。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/507102.html
