odata.net
odata.net copied to clipboard
Why ODataError.ErrorCode instead of ODataError.Code
While reading the OASIS error response specification I noticed that the ODataError has a ErrorCode property instead of a Code property.
Does a way exist to serialize ODataError with JsonConvert.SerializeObject so that it spits out a "code" field instead of the "errorCode" field?
I would like my services to be compliant with the spec. How do you folks managed that? Did some of you implement your own ODataError (that's what I'm currently thinking of)?
Ok, found a way to make JsonConvert do what I need with a custom contract resolver.
EDITED: also added conversion of "innerError" to "innererror" and "StackTrace" to "trace".
public sealed class CustomODataErrorContractResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
if (prop.PropertyName == "errorCode")
prop.PropertyName = "code";
else if (prop.PropertyName == "innerError")
prop.PropertyName = "innererror";
else if (prop.PropertyName == "stackTrace")
prop.PropertyName = "trace";
return prop;
}
}
static readonly JsonSerializerSettings ODataErrorJsonSerializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.None,
ContractResolver = new CustomODataErrorContractResolver()
};
Finally the ExceptionHandlerOptions for ASP Core's UseExceptionHandler:
public static ExceptionHandlerOptions GetCustomODataExceptionHandlerOptions(bool isDevelopment)
{
return new ExceptionHandlerOptions()
{
ExceptionHandler = async (context) =>
{
var feature = context.Features.Get<IExceptionHandlerFeature>();
context.Response.ContentType = "application/json";
var response = new
{
error = ODataErrorHelper.CreateODataError(feature.Error, context.Response.StatusCode, isDevelopment)
};
await context.Response.WriteAsync(JsonConvert.SerializeObject(response, ODataErrorJsonSerializerSettings));
}
};
}
@Casimodo72 Thanks for reaching us. yes, it's better to use "Code" as property name, however, it's a breaking change currently. We'd suppose to do it in next major release.
Please also note that the spec wants the ODataError property "InnerError" to be serialized as "innererror" (not "innerError") and the property "StackTrace" should be "trace". Non-breaking workaround: please consider adding a way to easily serialize an ODataError in compliance with the spec (e.g. dedicated contract resolver or even an extension method for JsonConvert).
It would also be great if people were made aware of this OData specification. I just fear that this will lead to lots of headaches w.r.t. interoperability with other OData services. Lots of programmers won't care/know and confuse/break error processing machineries. I didn't knew myself - until now.
ODataError.ToString() method outputs JSON that is not compliant with OData spec
Reproduce steps
The following code
var error = new ODataError(){ Message = "Error" }; var json = error.ToString();
outputs the following JSON:
{"error":{"code":"","message":"Error","target":"","details":{},"innererror":{} }}
Note that empty "details" property is written as an object but according to OData spec (and ODataError class) it is an array. The ODataMessageReader fails to read such error payloads.
Expected result
Empty OData error details property is serialized as JSON array.
{
"error": {
"code": "error code",
"message": "Unsupported functionality",
"target": "query",
"details": [
{
"code": "",
"target": "target",
"message": "message"
}
],
"innererror": {
"trace": [],
"context": {}
}
}
}
Actual result
What is actually happening.
{"error":{"code":"","message":"Error","target":"","details":{},"innererror":{} }}
Adding #1850 as a comment
As part of the 8.0 investigation, consider the following differences between the ODataError
type and what the standard requires:
- The standard requires that
code
be non-empty. ODL uses the empty string at least in cases wherenull
is provided, and maybe in additional cases - The standard requires that
message
be non-empty. ODL uses the empty string at least in cases wherenull
is provided, and maybe in additional cases - The standard allows
target
to benull
, but ODL does not have a direct option to allow this. - The standard requires that
details
be a JSON array, but when no details are provided, ODL uses{}
- The standard requires that the values within
details
be a complex type with propertiescode
,message
, andtarget
, but ODL useserrorcode
,message
, andtarget
- The standard has the same non-empty requirements for
code
andmessage
indetails
, but ODL has the same issues in this case as inODataError
- The standard has the same
null
allowance for thetarget
indetails
, but ODL has the same issues in this case as inODataError
- The standard allows for complete flexibility within the
innererror
property, but ODL requiresinnererrors
to have at least the same structure asODataError