Tuesday, March 28, 2017

ItemWebApi: maxJsonLength Property Exceeded

Sitecore's Item Web API  is extremely useful is so many ways - however as your database grows, you may encounter a limit on the size of the response Sitecore is able to return.

After installing the Sitecore Item Web API  feature to an existing Sitecore v6.6 site, I encountered an error when querying a huge part of the content tree using the following request:
/-/item/v1/?sc_database=master&scope=s&query=/sitecore/content//*

Due to the sheer amount of data the request attempted to retrieve, I received the following response:

"Error during serialization or deserialization using the JSON JavaScriptSerializer.
The length of the string exceeds the value set on the maxJsonLength property."


I initially figured the solution would be as simple as setting the MaxJsonLength property in the web.config, but soon realized that this setting had no real positive effect.

DISCOVERY

At this point, I took it in upon myself to decompile/reflect the Sitecore.ItemWebApi.dll in hopes of overriding the serialization method in order to set the MaxJsonLength property when initializing the JavaScriptSerializer.  

After some digging, I discovered two relevant methods I needed to override:

JsonSerializer

SerializeResponse

The SerializeResponse method serializes the API's response using JsonSerializer.Serialize to return the response in JSON format.

The Sitecore.ItemWebApi.config patch file defines the SerializeResponse pipeline:

SOLUTION

I started a new Website Project in Visual Studio, then
  1. Added the /App_Config/Include/zzz/ folders
  2. Copied over relevant binary files into a /References folder
    • Sitecore.ItemWebApi.dll
    • Sitecore.Kernel.dll (for Sitecore 6.6 - use your site's specific version*)
    • Newtonsoft.Json.dll (v3.5 for Sitecore 6.6  - use your site's specific version*)
  3. And added an Override folder to house the overriding code:



To override the existing Sitecore.ItemWebApi.Pipelines.Request.SerializeResponse definition, I created a patch file called zWebItemApiSerializedResponse.config and added it to the /App_Config/Include/zzz/ folder:

 <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">  
      <sitecore>  
          <pipelines>  
              <itemWebApiRequest>  
                <processor patch:instead="*[@type='Sitecore.ItemWebApi.Pipelines.Request.SerializeResponse, Sitecore.ItemWebApi']" type="Sitecore.SharedSource.ItemWebApiCustom.Override.SerializeResponseOverride, Sitecore.SharedSource.ItemWebApiCustom" />   
              </itemWebApiRequest>  
           </pipelines>  
      </sitecore>  
 </configuration>  

In the Override folder, I created two class files based on the decompiled Sitecore.ItemWebApi.dll  code:

SerializerOverride - inherits ISerializer
 using System;  
 using System.Web.Script.Serialization;  
 using Newtonsoft.Json;  
 using Sitecore.Diagnostics;  
 using Sitecore.ItemWebApi.Serialization;  
 using Formatting = System.Xml.Formatting;  
 namespace Sitecore.SharedSource.ItemWebApiCustom.Override  
 {  
   public class SerializerOverride : ISerializer  
   {  
     public string Serialize(object value)  
     {  
       Assert.ArgumentNotNull(value, "value");  
       var serializer = new JavaScriptSerializer {MaxJsonLength = Int32.MaxValue};  
       dynamic parsedJson = JsonConvert.DeserializeObject(serializer.Serialize(value));  
       return JsonConvert.SerializeObject(parsedJson, (Newtonsoft.Json.Formatting) Formatting.Indented);  
     }  
     public string SerializedDataMediaType => "application/json";  
   }  
 }  


SerializeResponseOverride - inherits SerializeResponse
 using Sitecore.Diagnostics;  
 using Sitecore.ItemWebApi.Pipelines.Request;  
 namespace Sitecore.SharedSource.ItemWebApiCustom.Override  
 {  
   public class SerializeResponseOverride : SerializeResponse  
   {  
     public override void Process(RequestArgs arguments)  
     {  
       Assert.ArgumentNotNull(arguments, "arguments");  
       SerializerOverride serializer = new SerializerOverride();  
       arguments.ResponseText = serializer.Serialize(arguments.Result);  
     }  
   }  
 }  


Once this project successfully built, I copied over the new App_Config file, and the projects Sitecore.SharedSource.ItemWebApiCustom.dll file to my solution's /bin folder (add the .pdb file to the /bin folder if you want to debug!)

RESULT

You can confirm your patch file is properly overriding the original SerializeResponse pipeline using /sitecore/admin/showconfig.aspx and searching for SerializeResponse on the page.

If everything went well, you can refresh the API call to see that the JSON response size restriction has been lifted!

Let me know in the comments if you have any suggestions or issues with this approach.
Also feel free to drop a line if this post has helped you out!



0 comments:

Post a Comment