Introduction:
When you happen to see this exception, i m sure you'll get n number of solutions in stackoverflow and trust me you've to understand your problem very well. There are scenarios based on which each of this solution works and differs.
Problem Statement:
Json Request body max length issue if in my case was sending large file in encoding format.
we used html 5 filereader api. There could be better solution to handle this we would have used HttpPostedFileBase So on.
But we had some custom implementation and some design challenges. And the story is i ended up with below exception.
Days of research helped me come up with different
solution.
Exception:
- System.ArgumentException: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.
- Parameter name: input
- at System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit)
- at System.Web.Mvc.JsonValueProviderFactory.GetDeserializedObject(ControllerContext controllerContext)
- at System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext)
- at Castle.Proxies.Invocations.ValueProviderFactory_GetValueProvider.InvokeMethodOnTarget()
- at Castle.DynamicProxy.AbstractInvocation.Proceed()
- at Glimpse.Core.Extensions.AlternateMethodContextExtensions.TryProceedWithTimer(IAlternateMethodContext context, TimerResult& timerResult)
- at Glimpse.Core.Extensibility.AlternateMethod.NewImplementation(IAlternateMethodContext context)
- at Castle.DynamicProxy.AbstractInvocation.Proceed()
- at Castle.Proxies.ValueProviderFactoryProxy.GetValueProvider(ControllerContext controllerContext)
- at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)
- at System.Web.Mvc.ControllerBase.get_ValueProvider()
- at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
- at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
- at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
Solution that didn't work for me:
No web.config settings helped to resolve my issue.
Don't know why
Solution That helped.
Approach - Solution 1:
Created Custom Value provider as per below stackoverflow answers.
http://stackoverflow.com/questions/9943727/jsonmaxlength-exception-on-deserializing-large-json-objects
The only problem is this woul applied to all controller and all action. Which is kind of security threat as per microsoft they explicetly apply maxJsonlength restriction. So again I want a solution which is very specific to Action of the controller.
Approach - Solution 2:
Create custom Model binder or Action Filter specific to controller. Due to architecture of our solution this was causing some problem.
Create custom Model binder or Action Filter specific to controller. Due to architecture of our solution this was causing some problem.
In ASP.NET MVC, deserialize JSON prior to or in controller's action method
http://stackoverflow.com/questions/1474896/in-asp-net-mvc-deserialize-json-prior-to-or-in-controllers-action-method
The above solution was not compatible with solution architecture. It is also a compromise of custom validation that we had on model.
Approach 3 : Solution [Cooked Up- Worked For me]
Phill Haack blog caught my eye. It enlightened me to some sort of brute force solution which in a way suits the overall design setup. This is win win situation.
Sending JSON to an ASP.NET MVC Action Method Argument
http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx/
This liner was breakthrough- Valueprovider vs ModelBinder
Jonathan had the unique insight to write a custom value provider which received JSON data and serialized it to a dictionary rather than the target object. The beauty of his approach is that this dictionary data is then passed to the default model binder which binds it to the final object with validation!
What worked?
I added custom deserialize json object with Maxvalue in override initialize method of MVC Controller.
protected override void Initialize(RequestContext requestContext) { if ("Some specific Action") { requestContext.HttpContext.Request.InputStream.Position = 0; StreamReader reader = new StreamReader(requestContext.HttpContext.Request.InputStream); string bodyText = reader.ReadToEnd(); if (String.IsNullOrEmpty(bodyText)) { // no JSON data } JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.MaxJsonLength = 2147483647; object model = serializer.DeserializeObject(bodyText); Dictionary < string, object > backingStore = new Dictionary < string, object >(StringComparer.OrdinalIgnoreCase); AddToBackingStore(backingStore, String.Empty, model); ValueProvider = new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture); } else { base.Initialize(requestContext); } }
I took AddToBackingStore method implementation from below post.
http://stackoverflow.com/questions/9943727/jsonmaxlength-exception-on-deserializing-large-json-objects
I added this..to overcome httpclient timeout,
Client.HttpClient.Timeout = new TimeSpan(1, 1, 1);
http://stackoverflow.com/questions/9943727/jsonmaxlength-exception-on-deserializing-large-json-objects
After few struggle my api was working fine but with few roadblock and hiccup ahead.
I realized after fetching data from client side to my action controller i was hitting other api to post this data again. There is long design story , please don't ask me why.. The request was timing out.
I have to add this.. mind it..this is just specific to this action and calling api stuff only not impacting other module. This is stop gap arrangement.
I added this..to overcome httpclient timeout,
Client.HttpClient.Timeout = new TimeSpan(1, 1, 1);
// Serialize Request string _requestContent = null; _requestContent = SafeJsonConvert.SerializeObject(request, this.Client.SerializationSettings); _httpRequest.Content = new StringContent(_requestContent, Encoding.UTF8); _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); // Send Request if (_shouldTrace) { ServiceClientTracing.SendRequest(_invocationId, _httpRequest); } cancellationToken.ThrowIfCancellationRequested(); Client.HttpClient.Timeout = new TimeSpan(1, 1, 1); _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false);
Some Configuration: Revisited.
IIS Web server setting
system.webServer
security
requestFiltering
requestLimits maxAllowedContentLength="6291456" /
/requestFiltering
/security
/system.webServer
httpRuntime maxRequestLength="51200" executionTimeout="3600" enableKernelOutputCache="false" relaxedUrlToFileSystemMapping="true" enableVersionHeader="false"
Server Script timeout
http://stackoverflow.com/questions/579523/how-do-i-set-the-request-timeout-for-one-controller-action-in-an-asp-net-mvc-app
HttpContext.Server.ScriptTimeout = 300; Or location path="ControllerName/ActionName"
jsonSerialization maxJsonLength
http://stackoverflow.com/questions/5692836/maxjsonlength-exception-in-asp-net-mvc-during-javascriptserializer
This is more applicable when the JsonResult is huge
This is more applicable when the JsonResult is huge
system.web.extensions
scripting
webServices>
jsonSerialization maxJsonLength="2147483644"/
/webServices
This is more applicable when the Json Request is huge . This didn't work most of the cases. So not fruitful solution whereas this globally applies to all controller action. Security threat.
appSettings
add
key="aspnet:MaxJsonDeserializerMembers" value="150000"
/
/appSettings
Few more story to hear. If you have patience to go through.Its worth reading.
http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
Search History:
Why MaxJson was not set to heavenly large value? DOS Attack
https://support.microsoft.com/en-in/kb/2661403
https://support.microsoft.com/en-us/kb/981884
https://social.msdn.microsoft.com/Forums/en-US/c017433c-0872-4689-a558-f0c2f301da10/javascriptserializer-deserialize-large-dictionary?forum=netfxnetcom
What Stackoverflow to offer?
Model Binder - To Deserialize the Json Object
http://stackoverflow.com/questions/13746472/using-modelbinder-attribute-vs-modelbinders-add
Custom Model binder derived from DefaulModelBinder
http://stackoverflow.com/questions/25577747/modelbinder-deserialize-arbitrary-json
In ASP.NET MVC, deserialize JSON prior to or in controller's action method
http://stackoverflow.com/questions/1474896/in-asp-net-mvc-deserialize-json-prior-to-or-in-controllers-action-method
Custom Value Provider
http://stackoverflow.com/questions/9943727/jsonmaxlength-exception-on-deserializing-large-json-objects
No comments :
Post a Comment