Is it possible, to override default Net.Core JsonSerializer or catch Exception on serialize?
Actually, when i pass wrong field type to model, required in controler(for example property is type of string i will pass int) then response look like this
{
"status": 400,
"message": "Validation error",
"data": [ {
"filedName": "$.Username",
"validateErrors": ["The JSON value could not be converted to System.String. Path: $.Username | LineNumber: 0 | BytePositionInLine: 16."]
}]
}
when i print this message to user, he won't know what i'm talking about. I need to ovveride this message for "Value is incorrect. Required [type]"
I have custom Provider which implement IValidationMetadataProvider to customize response on model validation error, but those error is not catching there.
You have three options
Catch the Json exception in server middleware, interrogate it, and then convert it to something more succint for the client. Search up "app.UseMiddleware<>()".
Inspect the status code of the HttpResponse message on the client, interrogate it, and then convert it to something more succinct for the UI.
Perform rudimentary format / variable type validations on the client before sending to the controller / API.
I would recommend approach 3. The API has presented a contract to you for the payload. The least the client / UI can do is make sure it complies with the contract. Failures due to business logic / rules definitely do belong on the server.
Related
I have parameter classes with the #Searializable annotation:
#Serializable
data class ShowPostURL(
val date: String,
val titleSlug: String,
override val redirectTo: String? = null
)
and no matter what I do call.receive() won't work. I'm getting HTTP 415 errors and Ktor doesn't log anything. I've added the serialization support as well:
install(ContentNegotiation) {
json()
}
How do I fix this? This is how I'm trying to use it:
accept(ContentType.Any) {
get("/foo/{date}/{titleSlug}") {
val input = call.receive(ShowPostURL::class)
call.respondText("foo")
}
}
If I do a trace I can see that my route is matched, but it can't receive the parameters. Is this json() setup is supposed to work when I'm deserializing from url parameters like this?
Firstly, ContentNegotiation feature works only for receiving custom objects from the payload of POST, PUT and PATCH requests:
POST, PUT and PATCH requests have an associated request body (the payload). That payload is usually encoded.
In order to receive custom objects from the payload, you have to use the ContentNegotiation feature. This is useful for example to receive and send JSON payloads in REST APIs.
When receiving, the Content-Type of the request will be used to determine which ContentConverter will be used to process that request
Secondly, there are three out of the box ContentConverter available:
GsonConverter, JacksonConverter and SerializationConverter.
Each of these converters has its own configuration function: gson, jackson and serialization respectively. You use json configuration function which is most likely is not appropriate for the configuration of ContentNegotiation.
To solve your problem you can access URL parameters by referring them with call.parameters and manually create ShowPostURL object. Then serialize it with the kotlinx.serialization framework if needed.
Also, you can write your own ContentConverter to implement custom logic for receiving typed objects.
I have a .net core 2.2 webapi. There is a POST action that accepts a model. The model has a Guid as one of the properties. When I post this model but supply a string and not a Guid, I get a ModelState.IsValid = false, which is correct. The default model binding error message is "Error converting value \"string\" to type 'System.Guid'. Path 'memberId', line 3, position 22." This is not a friendly message that I want to return, also, this message needs to get localized to user's language. All the resources that I have read said I need to set the accessor for ModelBindingMessageProvider in the AddMvC() options. i.e.
services.AddMvc(options => options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) => "blah blac");
I have set ALL of the accessors on there and it still doesn't change the default message. Anyone knows how to set those default values?
The issue is that the InputFormatter is throwing exception and the exception message is used for the modelstate entry. You can disable this in services.AddMvc().AddJsonOptions(options => options.AllowInputFormatterExceptionMessages = false;). This will add an empty string for the error message, which you can detect and then just display a generic message to user. I have not found a better way of doing this but this method will suffice for now.
Lets assume POST action method that expects json parameter like:
{
t:25
}
Case 1) - if we send something like
{
t:
then action should return 415 - Unsupported Media Type as parser won't be able to understand json (as its incomplete)
Case 2) - if we sent something like
{
t:'124'
}
so, sending string for property that is defined as int (in expected action model), we should return 422 - Unprocessable Entity as its a validation problem.
But, for both cases we get the same ModelState.Exception of type Newtonsoft.Json.JsonReaderException.
Question is, how to distinguish from those two cases?
For my API, I need to respond every request's error with HTTP 200 and a JSON content. So instead of, responding with this:
Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I'd like to do this:
res = {"code": 400, "message": "Bad Requset"}
Response(res)
Where is the best place to put such thing and how? In Serializer, Renderer, Exception? I need to catch every exception that serializer might throw as well as custom exception that I have written.
You may use status code 200 and with data=json.dumps(...), something like this:
res = {"code": 400, "message": "Bad Requset"}
return Response(data=json.dumps(res), status=status.HTTP_200_OK)
In terms of where to handle the exception, RestFramework has it covered, read Exceptions - Django REST framework, you can create a custom handler and do so.
However as api end points normally will be per view base, I would personally recommend create a custom decorator to wrap your views and return such Response in case of error.
Or, if you really want to return same response on ALL errors, then follow RestFramework doc and customise your error handling should be the best practice.
Hope this helps.
I'm using NHibernate.Validator with the Loquacious ValidationDef configuration style.
For all my rules I use WithMessage to set a custom message when the value is invalid. The messages are codes that I use to lookup the correct message to display, depending on context and language.
I can get these messages when calling ValidatorEngine.Validate(entity), but when saving an entity with NHibernate, I get an InvalidStateException with no details on why it's invalid.
So, how can I get to my validation messages after catching an InvalidStateException thrown during an NHibernate save?
The messages are in the exception, only a method call away. You need to call GetInvalidValues() on the exception.
try
{
// Flush NHibernate to invoke event listeners
}
catch (InvalidStateException invalidStateException)
{
InvalidValue[] invalidValues = invalidStateException.GetInvalidValues();
Console.WriteLine(string.Join<InvalidValue>("\n", invalidValues));
}
The reason they did not put it directly into Message of the exception, is probably because there can be multiple validation results.