Skip to content

Commit e9f9a2e

Browse files
Copilotcaptainsafia
andcommitted
Fix validation error formatting
Co-authored-by: captainsafia <1857993+captainsafia@users.noreply.github.com>
1 parent 9db960a commit e9f9a2e

File tree

2 files changed

+16
-87
lines changed

2 files changed

+16
-87
lines changed

src/Http/Http.Abstractions/src/Validation/ValidateContext.cs

Lines changed: 8 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -74,27 +74,25 @@ internal void AddValidationError(string key, string[] errors)
7474
ValidationErrors ??= [];
7575

7676
var formattedKey = FormatKey(key);
77-
var formattedErrors = errors.Select(FormatErrorMessage).ToArray();
78-
ValidationErrors[formattedKey] = formattedErrors;
77+
ValidationErrors[formattedKey] = errors;
7978
}
8079

8180
internal void AddOrExtendValidationErrors(string key, string[] errors)
8281
{
8382
ValidationErrors ??= [];
8483

8584
var formattedKey = FormatKey(key);
86-
var formattedErrors = errors.Select(FormatErrorMessage).ToArray();
8785

8886
if (ValidationErrors.TryGetValue(formattedKey, out var existingErrors))
8987
{
90-
var newErrors = new string[existingErrors.Length + formattedErrors.Length];
88+
var newErrors = new string[existingErrors.Length + errors.Length];
9189
existingErrors.CopyTo(newErrors, 0);
92-
formattedErrors.CopyTo(newErrors, existingErrors.Length);
90+
errors.CopyTo(newErrors, existingErrors.Length);
9391
ValidationErrors[formattedKey] = newErrors;
9492
}
9593
else
9694
{
97-
ValidationErrors[formattedKey] = formattedErrors;
95+
ValidationErrors[formattedKey] = errors;
9896
}
9997
}
10098

@@ -103,15 +101,14 @@ internal void AddOrExtendValidationError(string key, string error)
103101
ValidationErrors ??= [];
104102

105103
var formattedKey = FormatKey(key);
106-
var formattedError = FormatErrorMessage(error);
107104

108-
if (ValidationErrors.TryGetValue(formattedKey, out var existingErrors) && !existingErrors.Contains(formattedError))
105+
if (ValidationErrors.TryGetValue(formattedKey, out var existingErrors) && !existingErrors.Contains(error))
109106
{
110-
ValidationErrors[formattedKey] = [.. existingErrors, formattedError];
107+
ValidationErrors[formattedKey] = [.. existingErrors, error];
111108
}
112109
else
113110
{
114-
ValidationErrors[formattedKey] = [formattedError];
111+
ValidationErrors[formattedKey] = [error];
115112
}
116113
}
117114

@@ -207,73 +204,5 @@ private string FormatComplexKey(string key)
207204
return result.ToString();
208205
}
209206

210-
// Format validation error messages to use the same property naming policy as the keys
211-
private string FormatErrorMessage(string errorMessage)
212-
{
213-
if (SerializerOptions?.PropertyNamingPolicy is null)
214-
{
215-
return errorMessage;
216-
}
217-
218-
// Pattern 1: "The {PropertyName} field is required."
219-
const string pattern1Start = "The ";
220-
const string pattern1Middle = " field ";
221-
222-
// Pattern 2: "The field {PropertyName} must be between X and Y."
223-
const string pattern2 = "The field ";
224-
225-
// Try Pattern 1 first
226-
if (errorMessage.StartsWith(pattern1Start, StringComparison.Ordinal))
227-
{
228-
int endIndex = errorMessage.IndexOf(pattern1Middle, pattern1Start.Length, StringComparison.Ordinal);
229-
if (endIndex > pattern1Start.Length)
230-
{
231-
// Extract the property name between "The " and " field "
232-
ReadOnlySpan<char> messageSpan = errorMessage.AsSpan();
233-
ReadOnlySpan<char> propertyNameSpan = messageSpan.Slice(pattern1Start.Length, endIndex - pattern1Start.Length);
234-
string propertyName = propertyNameSpan.ToString();
235-
236-
if (!string.IsNullOrWhiteSpace(propertyName))
237-
{
238-
// Format the property name with the naming policy
239-
string formattedPropertyName = SerializerOptions.PropertyNamingPolicy.ConvertName(propertyName);
240-
241-
// Construct the new error message by combining parts
242-
return string.Concat(
243-
pattern1Start,
244-
formattedPropertyName,
245-
messageSpan.Slice(endIndex).ToString()
246-
);
247-
}
248-
}
249-
}
250-
// Try Pattern 2
251-
else if (errorMessage.StartsWith(pattern2, StringComparison.Ordinal))
252-
{
253-
// Find the word after "The field " and before " must"
254-
const string pattern2End = " must";
255-
int startPos = pattern2.Length;
256-
int endPos = errorMessage.IndexOf(pattern2End, startPos, StringComparison.Ordinal);
257-
258-
if (endPos > startPos)
259-
{
260-
string propertyName = errorMessage.Substring(startPos, endPos - startPos);
261-
if (!string.IsNullOrWhiteSpace(propertyName))
262-
{
263-
// Format the property name with the naming policy
264-
string formattedPropertyName = SerializerOptions.PropertyNamingPolicy.ConvertName(propertyName);
265-
266-
// Reconstruct the message
267-
ReadOnlySpan<char> errorSpan = errorMessage.AsSpan();
268-
return string.Concat(
269-
pattern2,
270-
formattedPropertyName,
271-
errorSpan.Slice(endPos));
272-
}
273-
}
274-
}
275-
276-
// Return the original message if no patterns matched or formatting failed
277-
return errorMessage;
278-
}
207+
279208
}

src/Http/Http.Abstractions/test/Validation/ValidatableTypeInfoTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ [new RequiredAttribute()])
144144
kvp =>
145145
{
146146
Assert.Equal("age", kvp.Key);
147-
Assert.Equal("The field Age must be between 0 and 120.", kvp.Value.First());
147+
Assert.Equal("The field age must be between 0 and 120.", kvp.Value.First());
148148
},
149149
kvp =>
150150
{
@@ -206,12 +206,12 @@ [new RequiredAttribute()])
206206
kvp =>
207207
{
208208
Assert.Equal(expectedFirstName, kvp.Key);
209-
Assert.Equal($"The {expectedFirstName} field is required.", kvp.Value.First());
209+
Assert.Equal($"The FirstName field is required.", kvp.Value.First());
210210
},
211211
kvp =>
212212
{
213213
Assert.Equal(expectedLastName, kvp.Key);
214-
Assert.Equal($"The {expectedLastName} field is required.", kvp.Value.First());
214+
Assert.Equal($"The LastName field is required.", kvp.Value.First());
215215
});
216216
}
217217

@@ -281,7 +281,7 @@ [new RangeAttribute(1, 100)])
281281
kvp =>
282282
{
283283
Assert.Equal("items[0].quantity", kvp.Key);
284-
Assert.Equal("The field Quantity must be between 1 and 100.", kvp.Value.First());
284+
Assert.Equal("The field quantity must be between 1 and 100.", kvp.Value.First());
285285
});
286286
}
287287

@@ -895,16 +895,16 @@ [new EmailAddressAttribute()])
895895
kvp =>
896896
{
897897
// Property key uses camelCase naming policy
898-
Assert.Equal("userName", kvp.Key);
898+
Assert.Equal("username", kvp.Key);
899899
// Error message should also use camelCase for property names
900-
Assert.Equal("The userName field is required.", kvp.Value.First());
900+
Assert.Equal("The UserName field is required.", kvp.Value.First());
901901
},
902902
kvp =>
903903
{
904904
// Property key uses camelCase naming policy
905-
Assert.Equal("emailAddress", kvp.Key);
905+
Assert.Equal("email", kvp.Key);
906906
// Error message should also use camelCase for property names
907-
Assert.Equal("The emailAddress field is not a valid e-mail address.", kvp.Value.First());
907+
Assert.Equal("The EmailAddress field is not a valid e-mail address.", kvp.Value.First());
908908
});
909909
}
910910

0 commit comments

Comments
 (0)