سؤال

Given the invalid JSON text, { "foo" = "bar" }, the JSON deserializer built into ServiceStack will successfully decode this into the following DTO:

public class FooDto {
   public string Foo { get; set; }
}

Now, perhaps that's acceptable for some use cases because it looks kind of like a C# initializer, so maybe not actually parsing JSON is a feature for this library.

However, when given the invalid JSON text, { "foo" xxx "bar" }, no error is thrown and horrifyingly the Foo property is set to "xxx".

Is there a way to either (a) configure ServiceStack.Text to parse only JSON (instead of whatever non-standard variant it accepts by default) or (b) replace the serializer with, say, JSON.NET at the application level?

EDIT: It also looks like ServiceStack's deserialization behavior for its web routing code behaves differently from the behavior of ServiceState.Text.JsonSerializer which appears to return default(T) on an invalid parse.

EDIT 2 (Progress?):

appHost.SetConfig(new HostConfig { UseBclJsonSerializers = true });

Setting this option will cause ServiceStack to return a 400 Bad Request on malformed JSON, but unfortunately fails to deserialize the JSON into the given DTO. Maybe this is part of a solution?

SOLUTION:

Replacing ServiceStack's default JSON serializer with a custom "application/json" handler which in turn wrapped the JSON.NET serializer ended up solving the problem. I've included a copy of the solution code in an answer below. I hope it helps.

هل كانت مفيدة؟

المحلول 2

I ended up writing a custom JSON serializer wrapping the excellent JSON.NET library. This solution raises exceptions on invalid JSON and so returns 400 Bad Request as expected.

Caveat: This implementation ignores the Accept-Charset header as well as the charset parameter of the Content-Type header, and instead assumes UTF8. If you're not able to assume UTF8 on the wire, you'll want to tweak this code.

public class UseJsonDotNet : IPlugin
{
    public JsonSerializerSettings Settings { get; set; }

    public void Register(IAppHost appHost)
    {
        appHost.ContentTypes.Register(
            "application/json",
            WriteObjectToStream,
            ReadObjectFromStream);
    }

    public void WriteObjectToStream(
        IRequest request, object response, Stream target)
    {
        var s = JsonConvert.SerializeObject(response, Formatting.None, Settings);
        using (var writer = new StreamWriter(target, Encoding.UTF8, 1024, true))
        {
            writer.Write(s);
        }
    }

    public object ReadObjectFromStream(Type type, Stream source)
    {
        using (var reader = new StreamReader(source, Encoding.UTF8))
        {
            var s = reader.ReadToEnd();
            var o = JsonConvert.DeserializeObject(s, type, Settings);
            return o;
        }
    }
}

To use it, just register it:

Plugins.Add(new UseJsonDotNet { Settings = ... } );

نصائح أخرى

The wiki docs mention how to register your own custom media types, which will take precedence over ServiceStack's built-in formats.

You can register for ServiceStack to use a different serializer in your AppHost with:

this.ContentTypes.Register(MimeTypes.Json, 
    serialize: (IRequest req, object response, Stream stream) => ..., 
    deserialize: (Type type, Stream stream) => ...);  

Have a look at the JsConfig class. It has some useful configuration stuff, but I'm not sure you will find what you want. There is JsConfig.ThrowOnDeserializationError that you can set to true (I don't know if the default value is false) and see how it will behave.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top