想像使用者在 TaskFlow 新增任務時,輸入了以下資料:
{
"title": ""
}
或者輸入一個長達 500 字的任務名稱。
如果沒有設定限制,這些資料就可能被儲存到資料庫中,造成資料品質不佳,甚至影響後續功能。
因此,我們要讓 TaskFlow 檢查使用者輸入的資料。
今天設定三個規則:
Data Annotations 是 C# 提供的一組 Attribute(屬性標記)。
使用昨日的專案
using System.ComponentModel.DataAnnotations;
namespace TaskFlow3.Api.DTOs
{
public class CreateTaskDto
{
[Required(ErrorMessage = "任務名稱不可空白")]
[StringLength(
100,
MinimumLength = 2,
ErrorMessage = "任務名稱必須介於 2 到 100 個字元"
)]
public string Title { get; set; } = "";
}
}
using System.ComponentModel.DataAnnotations;
namespace TaskFlow3.Api.DTOs
{
public class UpdateTaskDto
{
[Required(ErrorMessage = "任務名稱不可空白")]
[StringLength(
100,
MinimumLength = 2,
ErrorMessage = "任務名稱必須介於 2 到 100 個字元"
)]
public string Title { get; set; } = "";
public bool IsCompleted { get; set; }
}
}
using Microsoft.AspNetCore.Mvc;
using TaskFlow3.Api.DTOs;
using TaskFlow3.Api.Models;
using TaskFlow3.Api.Services;
namespace TaskFlow3.Api.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class TasksController : ControllerBase
{
private readonly ITaskService _taskService;
// 建構函式注入
public TasksController(ITaskService taskService)
{
_taskService = taskService;
}
// GET /api/tasks
// 查詢所有任務,支援篩選、搜尋、排序
[HttpGet]
public async Task<ActionResult<List<TaskDto>>> GetTasks(
[FromQuery] bool? completed = null,
[FromQuery] string? keyword = null,
[FromQuery] string sort = "asc")
{
var tasks = await _taskService.GetAllAsync(
completed,
keyword,
sort
);
var result = tasks.Select(t => ToDto(t)).ToList();
return Ok(result);
}
// GET /api/tasks/1
// 查詢指定任務
[HttpGet("{id:int}")]
public async Task<ActionResult<TaskDto>> GetTask(int id)
{
TaskItem? task = await _taskService.GetByIdAsync(id);
if (task == null)
{
return NotFound(new
{
message = "找不到這筆任務",
taskId = id
});
}
return Ok(ToDto(task));
}
// POST /api/tasks
// 新增任務
[HttpPost]
public async Task<ActionResult<TaskDto>> CreateTask(
[FromBody] CreateTaskDto input)
{
// 檢查任務名稱是否為空白
if (string.IsNullOrWhiteSpace(input.Title))
{
ModelState.AddModelError(
nameof(input.Title),
"任務名稱不可空白"
);
return ValidationProblem(ModelState);
}
// 移除前後空白
string title = input.Title.Trim();
// 檢查移除空白後的長度
if (title.Length < 2 || title.Length > 100)
{
ModelState.AddModelError(
nameof(input.Title),
"任務名稱必須介於 2 到 100 個字元"
);
return ValidationProblem(ModelState);
}
// 呼叫 Service 新增任務
TaskItem task = await _taskService.CreateAsync(title);
return CreatedAtAction(
nameof(GetTask),
new { id = task.Id },
ToDto(task)
);
}
// PUT /api/tasks/1
// 修改任務
[HttpPut("{id:int}")]
public async Task<IActionResult> UpdateTask(
int id,
[FromBody] UpdateTaskDto input)
{
// 檢查任務名稱是否為空白
if (string.IsNullOrWhiteSpace(input.Title))
{
ModelState.AddModelError(
nameof(input.Title),
"任務名稱不可空白"
);
return ValidationProblem(ModelState);
}
string title = input.Title.Trim();
// 檢查名稱長度
if (title.Length < 2 || title.Length > 100)
{
ModelState.AddModelError(
nameof(input.Title),
"任務名稱必須介於 2 到 100 個字元"
);
return ValidationProblem(ModelState);
}
// 呼叫 Service 修改資料
bool success = await _taskService.UpdateAsync(
id,
title,
input.IsCompleted
);
if (!success)
{
return NotFound(new
{
message = "找不到這筆任務",
taskId = id
});
}
return NoContent();
}
// DELETE /api/tasks/1
// 刪除任務
[HttpDelete("{id:int}")]
public async Task<IActionResult> DeleteTask(int id)
{
bool success = await _taskService.DeleteAsync(id);
if (!success)
{
return NotFound(new
{
message = "找不到這筆任務",
taskId = id
});
}
// 刪除成功,回傳 204
return NoContent();
}
// 將 Model 轉換成 DTO
private static TaskDto ToDto(TaskItem task)
{
return new TaskDto
{
Id = task.Id,
Title = task.Title,
IsCompleted = task.IsCompleted
};
}
}
}
資料驗證不只是阻止錯誤資料進入系統,也需要讓前端知道發生什麼問題。
400:使用者傳送的資料有問題。
404:使用者要求的任務不存在。
500:伺服器內部發生未預期錯誤。