Stop throwing for not-found; add Guard and request validation
Not-found lookups return null/false instead of throwing NotFoundException across all services — a missing row is expected control flow, not an exceptional condition. NotFoundException stays for embedded precondition checks inside mutations (missing parent, invalid foreign reference). Guard (copied from mic-check) enforces required arguments at the top of every service method. A ported IModelValidator<T> framework validates every request DTO at the API layer via a new ValidationEndpointFilter, returning a 400 with field-level messages; services re-run the same validator and throw for direct callers that bypass the API. Endpoints translate null/false into 404 via a new ToApiResult() helper. The agent toolset boundary translates the same nullable/bool results into the tool-error text the model already expected.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
namespace Novelly.Api.Common.Validation;
|
||||
|
||||
public static class ValidationResultExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// The service-level half of "validate again and throw if invalid": callers that reach
|
||||
/// a service directly (agent tools, MCP, tests) skip the API's <see cref="ValidationEndpointFilter"/>,
|
||||
/// so services re-run the same validator and throw rather than act on bad data.
|
||||
/// </summary>
|
||||
public static void ThrowIfInvalid(this ValidationResult result)
|
||||
{
|
||||
if (result.IsInvalid)
|
||||
{
|
||||
throw new ArgumentException(string.Join("; ", result.Errors.Select(e => $"{e.PropertyName}: {e.Message}")));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user