5

I have a model object that I send to the browser and gets sent back to me. I want that ID value in that object to be encrypted. I created a custom JsonConverter to encrypt the string and then decrypt it.

public class SecretItem
{
    [JsonConverter(typeof(EncryptedIdConverter))]
    public string Id { get; set; }
    public string Name { get; set; }
}

This is my EncryptedIdConverter class

class EncryptedIdConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        string encryptedValue = (string)value;

        if (!string.IsNullOrWhiteSpace(encryptedValue))
            encryptedValue = Encryption.EncryptString(encryptedValue);

        serializer.Serialize(writer, encryptedValue);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        string decryptedString = (string)reader.Value;

        if (!string.IsNullOrWhiteSpace(decryptedString))
            decryptedString = Encryption.DecryptString(decryptedString);

        return decryptedString;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(string).IsAssignableFrom(objectType);
    }
}

If I try calling the JsonConvert.Serialization functions, everything works correctly.

JsonConvert.SerializeObject(secretItem)
JsonConvert.DeserializeObject<SecretItem>([JSON secretItem]);

When I return the HttpActionResult Ok(secretItem)... the browser also gets the encrypted Id string.

However, when I POST the data back to my controller, my webapi method is not getting a decrypted property. It skips the JsonConverter.

public async Task<IHttpActionResult> Post(SecretItem secretItem)
{
    // Not decrypted
    var decryptedId = secretItem.Id;
}

Why would the deserialize logic not be working the same as the serialize logic in my WebAPI? I don't even know where to start debugging that.

We are using Newtonsoft.Json 10.0.0.0, MVC5, .NET Framework 4.6.1.

AmoebaMan17
  • 722
  • 1
  • 8
  • 22
  • Have you tried break-pointing your custom converter to make sure it's getting called? – Jonathon Chase Jan 08 '19 at 18:32
  • Yes. It gets called when I call JsonConvert directly. And it gets called during the serialization in WebAPI. I don't know why it is skipped during deserialization in the WebAPI. – AmoebaMan17 Jan 08 '19 at 18:35
  • what is your CanConvert method ? – Mohamed Elrashid Jan 08 '19 at 19:16
  • I added my EncryptedIdConverter class to the original post, since it was too long to add as a comment. – AmoebaMan17 Jan 08 '19 at 19:41
  • @MohamedElrashid When I added a breakpoint, I don't see the CanConvert method getting called during any of my serialization... when I return it from a controller and it gets serialized or when I call JsonConvert.De/SerializeObject directly. – AmoebaMan17 Jan 08 '19 at 19:49
  • I think you need to tell Web API that its serializer should make use of that converter. Add it to the serializer's `Converters` collection in your global.asax. –  Jan 08 '19 at 20:14
  • @Amy I did try that and I think it got called for all string values then. – AmoebaMan17 Jan 08 '19 at 20:30
  • When I put a breakpoint in Newtonsoft's JsonSerializer, I don't even see the deserialize logic being called when my http request comes in. Maybe WebAPI doesn't call that for simple objects? – AmoebaMan17 Jan 08 '19 at 20:34

2 Answers2

4

It turns out the code is working correctly. The problem was that on the POST that was being tested, the content-type wasn't set to "application/json". So, it didn't use the JsonNetFormatter and therefore skipped the converter.

Once I set the contentType, everything works!

AmoebaMan17
  • 722
  • 1
  • 8
  • 22
2

How is your Global.asax

protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  GlobalConfiguration.Configure(WebApiConfig.Register);
  FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
  RouteConfig.RegisterRoutes(RouteTable.Routes);
  BundleConfig.RegisterBundles(BundleTable.Bundles);
}

it should work

maybe you need a TypeConverter

Model binding

When Web API calls a method on a controller, it must set values for the parameters, a process called binding

  • This is called Model binding

     Post(SecretItem secretItem)
    
  • Model binding use a TypeConverter

JSON Serialization

  • This is called JSON Serialization

    HttpActionResult Ok(secretItem)
    
  • JSON Serialization use a JsonConverter

Documentation

more

Mohamed Elrashid
  • 8,125
  • 6
  • 31
  • 46