I'm trying to use JsonSerializerSettings for custom error handling, but when I specify the object type the error stops in runtime debugging. The "json" is no valid JSON, due to remote error that is out of my hands to change/fix.
Working:
var responseData = JsonConvert.DeserializeObject(json,new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
Error = (sender, args) =>
{
// My error handling
}
});
Breaks With:
Additional information: Error converting value "Received merchantid does not match a registered merchant" to type 'TransmitModels+errorData'. Path ...
TransmitModels.errorData responseData = JsonConvert.DeserializeObject<TransmitModels.errorData>(json,new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
Error = (sender, args) =>
{
// My error handling
}
});
You need to call
args.ErrorContext.Handled = true;
in your callback to tell Json.NET that you handled the exception. If you don't (maybe because you just want to log the error), the exception is thrown after your callback.
Related
I've a method that deserialize string into a type.
var data = JsonConvert.DeserializeObject<TestData>("invalid json");
If string is not valid, JsonReaderException occurring.
I want to return default value of TestData (null) when string is not valid instead of throw exception.
How can I do this without try/catch and JObject?
You can use JsonSerializerSettings to handle it the result of it will be NULL.
The reference of JsonConvert.DeserializeObject and JsonSerializerSettings.Error
var settings = new JsonSerializerSettings
{
Error = (se, ev) =>
{
ev.ErrorContext.Handled = true;
}
};
var data = JsonConvert.DeserializeObject<Currency>("invalid json", settings);
In Asp.Net Core 5 I am using UseExceptionHandler to handle exceptions globally and it works fine unless I send an invalid object. For example I send an object with null value for the required property "Name" I receive the following error in client but the Run function does not hit in debugger.
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1" ,"title":"One
or more validation errors
occurred.","status":400,"traceId":"00-2428f0fb9c415843bca2aaef08eda6f6-11ea476efb792849-00","errors":{"Name":["The
field Name is required"]}}
(as the first middleware in the pipeline :)
app.UseExceptionHandler(a => a.Run(async context =>
{
//this does not being exceuted for validation errors
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = exceptionHandlerPathFeature.Error;
var exceptionManager = a.ApplicationServices.GetRequiredService<IExceptionManager>();
await context.Response.WriteAsJsonAsync(exceptionManager.Manage(exception, context));
}));
This may help you.
services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var errors = context.ModelState.Keys
.SelectMany(key => context.ModelState[key].Errors.Select(x => $"{key}: {x.ErrorMessage}"))
.ToArray();
var apiError = new CustomErrorClass()
{
Message = "Validation Error",
Errors = errors
};
var result = new ObjectResult(apiError);
result.ContentTypes.Add(MediaTypeNames.Application.Json);
return result;
};
});
After updating NServiceBus to the latest version released last week (which also has a new implementation), I'm seeing weird errors when sending json from the client.
I will send a message from the client and the receiver displays this message:
2016-10-18 22:16:33.612 INFO MFG.Receiver.DeviceHandler Got message with id: a222b136-6a4e-474e-8012-cc1c24e5e539
I have a breakpoint in my handler, below, and it shows the message object is baked and there should not be any issues.
public class DeviceHandler : IHandleMessages<DeviceRequest>
{
private readonly IDeviceProvider _provider = new DeviceProvider();
private static readonly ILog Log = LogManager.GetLogger<DeviceHandler>();
public Task Handle(DeviceRequest message, IMessageHandlerContext context)
{
Log.Info($"Got message with id: {context.MessageId}");
...
return context.SendLocal($"Message with Id {context.MessageId} received.");
}
}
When it hits the reply method at the end, it throws the below errors:
2016-10-18 22:16:33.666 INFO NServiceBus.RecoverabilityExecutor Immediate Retry is going to retry message 'a222b136-6a4e-474e-8012-cc1c24e5e539' because of an exception:
System.Exception: Could not find metadata for 'System.String'.
Ensure the following:
1. 'System.String' is included in initial scanning.
2. 'System.String' implements either 'IMessage', 'IEvent' or 'ICommand' or alternatively, if you don't want to implement an interface, you can use 'Unobtrusive Mode'.
at NServiceBus.Unicast.Messages.MessageMetadataRegistry.GetMessageMetadata(Type messageType) in C:\Build\src\NServiceBus.Core\Unicast\Messages\MessageMetadataRegistry.cs:line 39
I'm not sure why it would throw the System.String error, after it already received the message from the handler and the properties are populated...
The json sent looks like this:
{
"$type": "DeviceRequest, MFG.Domain",
"Id": "devices-65",
"DeviceId": 1,
"Location": "Orlando",
"DeviceType": "test"
}
My Sender (client) looks like this:
static void Main()
{
...
using (var channel = connection.CreateModel())
{
var messageId = Guid.NewGuid().ToString();
var properties = channel.CreateBasicProperties();
properties.MessageId = messageId;
var payload = GenerateJsonPayload();
channel.BasicPublish(string.Empty, ServerEndpointName, false, properties, Encoding.UTF8.GetBytes(payload));
Console.WriteLine($"Message with id {messageId} sent to queue.");
}
...
}
public static string GenerateJsonPayload()
{
var obj = new DeviceRequest
{
DeviceId = 1,
DeviceType = "test",
Location = "Orlando"
};
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};
var result = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
return result;
}
I've had the "could not find metadata" issue before, and it was due to malformed json or not having type. If I remove the JsonSerializerSettings, and just passed a serialized object, I instead get errors:
2016-10-18 22:31:27.698 ERROR NServiceBus.RecoverabilityExecutor Moving message '6405179d-ea36-4264-af2a-704da19af120' to the error queue 'error' because processing failed due to an exception:
NServiceBus.MessageDeserializationException: An error occurred while attempting to extract logical messages from transport message 6405179d-ea36-4264-af2a-704da19af120 ---> System.Exception: Could not find metadata for 'Newtonsoft.Json.Linq.JObject'.
I have no idea what I'm missing here and this wasn't an issue with the previous version. Is this a bug or ... ?
Use a concrete message type for your SendLocal operation.
As part of handling the message you are doing return context.SendLocal($"Message with Id {context.MessageId} received.");. This will try to send a message of type "string" to the local queue. NServiceBus is telling you that no message metadata was registered for the message type of "string". Therefor it can not construct the message and throws an exception.
I have been venturing in the ServiceStack's documentation regarding an issue with throwing Exceptions from an Action that returns a Stream.
The issue is that while all the other Actions in my service return beautiful errors like:
{
"ResponseStatus": {
"ErrorCode": "ArgumentException",
"Message": "Unable to load data",
"StackTrace": "[GetData: 7/11/2016 1:02:11 PM]:\n[REQUEST: {Token:asdf,Id:1}]\nServiceStack.HttpError: Unable to load codes from token ---> System.ArgumentException: Unable to load codes from token.............(abridged)
}
}
There is an Action with the return type as Stream from which, regardless of the type of exception returned, the http client receives the following response:
With the handler (as per the SS documentation):
Error: ArgumentNullException: As result 'ErrorResponse' is not a supported responseType, a defaultAction must be supplied
Parameter name: defaultAction
And without any handlers:
'no content'
400
Any help will be greatly appreciated.
Sample Code-->
Here is an example of the Action:
[AddHeader(ContentType = "application/pdf")]
public Stream Get(GetPdfRequest request)
{
throw new NotImplementedException("FAKE EXCEPTION");
}
and in the APPHOST's Configure() method:
this.UncaughtExceptionHandlers.Add((req, res, operationName, ex) =>
{
var logger = LogManager.GetLogger(GetType());
logger.Error("Unhandled error in API during request binding.", ex);
res.Write("Error: {0}: {1}".Fmt(ex.GetType().Name, ex.Message));
res.EndRequest(skipHeaders: true);
});
this.ServiceExceptionHandlers.Add((httpReq, request, exception) =>
{
var logger = LogManager.GetLogger(GetType());
logger.Error("Unhandled error in API.", exception);
//call default SS exception handler
return DtoUtils.CreateErrorResponse(request, exception);
});
Here is a screenshot of what I see on the Swagger Rest client when the above Action is called.
The issue is due to being unable to serialize the ErrorResponse DTO into the unregistered "application/pdf" ContentType.
I've just added a fallback to use the Config.DefaultContentType for serializing errors in unregistered Content Types in this commit, available from v4.0.61 that's now available on MyGet.
A workaround for prior versions of ServiceStack is instead of using the [AddHeader] Request Filter Attribute, to instead set the Content-Type in the Service implementation just before you serialize, so any Exceptions are thrown before Response ContentType is set, e.g:
public class ErrorStream {}
public class ErrorStreamService : Service
{
public Stream Any(ErrorStream request)
{
if (!IsValid(request))
throw new NotImplementedException("Exception in Stream Response");
base.Request.ResponseContentType = "application/pdf";
return PdfAsStream(request);
}
}
Which throws a Typed Exception when using a Service Client:
try
{
var response = client.Get<Stream>(new ErrorStream());
Assert.Fail();
}
catch (WebServiceException ex)
{
Assert.That(ex.IsAny400());
Assert.That(!ex.IsAny500());
Assert.That(ex.ErrorCode, Is.EqualTo("NotImplementedException"));
Assert.That(ex.StatusCode, Is.EqualTo((int)HttpStatusCode.MethodNotAllowed));
}
Also UncaughtExceptionHandlers is only for handling Exceptions thrown outside of a Service, exceptions that occur within a Service are instead handled by ServiceExceptionHandlers instead, but be careful when modifying the default Exception handling behavior as you can invalidate the typed Exception handling on the client.
How do we get more information about the JSON De-serialization exceptions when ServiceStack's JSON Serializer is configured to throw on exceptions with:
JsConfig.ThrowOnDeserializationError = true;
By default the JSON Serializer will log and ignore non-critical exceptions or can be configured to throw with the above config.
When ServiceStack's JSON Serializer is configured to throw when it encounters a de-serialization error, with:
JsConfig.ThrowOnDeserializationError = true;
It captures the information it was able to de-serialize and stores them in the ex.Data Dictionary in the thrown Serialization Exception.
try {
string json = #"{""idBad"":""abc"", ""idGood"":""2"" }";
JsonSerializer.DeserializeFromString(json, typeof(TestDto));
Assert.Fail("Exception should have been thrown.");
} catch (SerializationException ex) {
Assert.That(ex.Data, Is.Not.Null);
Assert.That(ex.Data["propertyName"], Is.EqualTo("idBad"));
Assert.That(ex.Data["propertyValueString"], Is.EqualTo("abc"));
Assert.That(ex.Data["propertyType"], Is.EqualTo(typeof(int)));
}