A service can have sync or async
request methods. However manual validation exception returns different responses depending upon sync/async
. The sync request returns the correct response information in order to use ss-utils
to applyErrors()
. The async
request seems to return more generic information and the errors[]
array is empty.
With a sync request both .Validate(...)
and .ValidateAsync(...)
work correctly.
With an async
request both do not work (in that they return a generic validation error).
// sync (1) .Validate(...) Works Correctly
public class RegisterService : Service
{
public object Post(Register request)
{
// biz code
// manual validate
var validator = new RegisterValidator() { Request = Request };
var validationResult = validator.Validate(Request, request);
if (!validationResult.IsValid)
{
// Note: .ToException() returns custom FormValidationException code below
throw validationResult.ToException();
}
// more biz code
return response;
}
}
// sync (2) .ValidateAsync(...) Works Correctly
public class RegisterService : Service
{
public object Post(Register request)
{
// biz code
// manual validate
var validator = new RegisterValidator() { Request = Request };
var validationResult = validator.ValidateAsync(Request, request).Result;
if (!validationResult.IsValid)
{
// Note: .ToException() returns custom FormValidationException code below
throw validationResult.ToException();
}
// more biz code
return response;
}
}
// async (1) .ValidateAsync(...) Doesn't work - Returns generic `ValidationError` response.
public class RegisterService : Service
{
public async Task<object> Post(Register request)
{
// biz code
// manual validate
var validator = new RegisterValidator() { Request = Request };
var validationResult = await validator.ValidateAsync(Request, request);
if (!validationResult.IsValid)
{
// Note: .ToException() returns custom FormValidationException code below
throw validationResult.ToException();
}
// more biz code
return response;
}
}
// async (2) .Validate(...) Doesn't work - Returns generic `ValidationError` response.
public class RegisterService : Service
{
public async Task<object> Post(Register request)
{
// biz code
// manual validate
var validator = new RegisterValidator() { Request = Request };
var validationResult = validator.Validate(Request, request);
if (!validationResult.IsValid)
{
// Note: .ToException() returns custom FormValidationException code below
throw validationResult.ToException();
}
// more biz code
return response;
}
}
Validator code:
public class RegisterValidator : AbstractValidator<Register>
{
public RegisterValidator()
{
RuleSet(
ApplyTo.Put | ApplyTo.Post,
() =>
{
RuleFor(x => x.Email).EmailAddress();
});
}
}
Note: FormValidationError is custom, but isn’t necessary. Was trying to return additional info in the ValidationError
, but it isn’t relevant to the issue that’s happening.
public class FormValidationError : ValidationError
{
public FormValidationError(string errorCode) : base(errorCode) { }
public FormValidationError(ValidationErrorResult validationResult) : base(validationResult) { }
public FormValidationError(ValidationErrorField validationError) : base(validationError) { }
public FormValidationError(string errorCode, string errorMessage) : base(errorCode, errorMessage) { }
// Note: This doesn't get included in error response. Not sure why. Not the issue at the moment.
public string MyProp { get; set; }
}
sync requests return correctly with:
{
"JSON": {
"responseStatus": {
"errorCode": "Email",
"message": "'Email' is not a valid email address.",
\"errors\":[{\"errorCode\":\"Email\",\"fieldName\":\"Email\",\"message\":\"'Email' is not a valid email address.\",\"meta\":{\"PropertyName\":\"Email\",\"PropertyValue\":\"test@\"}}]}}",
"mode": "application/json"
}
}
}
stacktrace was removed from above json, but it was:
[Register: 10/17/2019 1:05:59 AM]:[REQUEST: {email:test@,continue:"https://localhost:44333/home/"}]<snip>.FormValidationError: 'Email' is not a valid email address. at <snip>\RegisterService.cs:line 172 at <snip>.RegisterService.Post(Register request) in <snip>\RegisterService.cs:line 84 at ServiceStack.Host.ServiceRunner`1.ExecuteAsync(IRequest req, Object instance, TRequest requestDto) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\Host\ServiceRunner.cs:line 133
async requests return the following json
{
"JSON": {
"responseStatus": {
"errorCode": "FormValidationError",
"message": "'Email' is not a valid email address.",
"stackTrace": " at <snip>.RegisterService.cs:line 172\r\n at ServiceStack.Host.Handlers.ServiceStackHandlerBase.HandleResponse(IRequest httpReq, IResponse httpRes, Object response) in C:\\BuildAgent\\work\\3481147c480f4a2f\\src\\ServiceStack\\Host\\Handlers\\ServiceStackHandlerBase.cs:line 81\r\n at ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest req, IResponse httpRes, String operationName) in C:\\BuildAgent\\work\\3481147c480f4a2f\\src\\ServiceStack\\Host\\RestHandler.cs:line 103"
}
},
"Response payload": {
"EDITOR_CONFIG": {
"text": "{\"responseStatus\":{\"errorCode\":\"FormValidationError\",\"message\":\"'Email' is not a valid email address.\",\"stackTrace\":\" at <snip>\\\\RegisterService.cs:line 172\\r\\n at ServiceStack.Host.Handlers.ServiceStackHandlerBase.HandleResponse(IRequest httpReq, IResponse httpRes, Object response) in C:\\\\BuildAgent\\\\work\\\\3481147c480f4a2f\\\\src\\\\ServiceStack\\\\Host\\\\Handlers\\\\ServiceStackHandlerBase.cs:line 81\\r\\n at ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest req, IResponse httpRes, String operationName) in C:\\\\BuildAgent\\\\work\\\\3481147c480f4a2f\\\\src\\\\ServiceStack\\\\Host\\\\RestHandler.cs:line 103\"}}",
"mode": "application/json"
}
}
}
Stack Trace
at <snip>\\\\RegisterService.cs:line 172\\r\\n at ServiceStack.Host.Handlers.ServiceStackHandlerBase.HandleResponse(IRequest httpReq, IResponse httpRes, Object response) in C:\\\\BuildAgent\\\\work\\\\3481147c480f4a2f\\\\src\\\\ServiceStack\\\\Host\\\\Handlers\\\\ServiceStackHandlerBase.cs:line 81\\r\\n at ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest req, IResponse httpRes, String operationName) in C:\\\\BuildAgent\\\\work\\\\3481147c480f4a2f\\\\src\\\\ServiceStack\\\\Host\\\\RestHandler.cs:line 103
Notice that the async
requests have ValidationError responses with an empty errors[]
array and errorCode: "FormValidationError
Sync requests have a populated/filled in errors[]
array and have an error code of errorCode: "Email"
.
It doesn’t matter if the sync request uses .ValidateAsync(...).Result
or the sync .Validate(...)
, it still works.
The difference or issue seems to be with whether the request is sync or async
.