Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery .ajax post fails with large JSON object

I am posting JSON data to an ASP.NET MVC2 server. I am posting large JSON strings (which incorporate a base64-encoded file stream, read from the local file system). The jQuery ajax call works fine up to file sizes of around 2.5Mb. Once above this size the ajax call fails (never gets to the controller). I can't detect exactly what the error is - it doesn not seem to populate the error variables.

The ajax call is as follows:

$.ajax({
            type: "POST",
            dataType: 'json',
            timeout: 10000,
            url: "/Molecule/SaveMolecule",
            data: { jsonpost: postdata, moleculetype: _moleculeType, moleculefilestream: _moleculefilestream, changedproducts: stringifiedChangedProducts }, // NOTE the moleculeType being added here
            success: function (data) {
                if (data.rc == "success") {
                    $.log('ServerSuccess:' + data.message);

                    molecule_updateLocalInstance();

                    _bMoleculeIsDirty = false;
                    if (bReturnToMoleculeList != null && bReturnToMoleculeList == true) {
                        navigator_Go('/Molecule/Index/' + _moleculeType);
                    }
                    else {
                        _saveMoleculeButtonFader = setTimeout(function () {

                            $('#profilesave-container').delay(500).html('<img src="/content/images/tick.png" width="32px" height="32px" /><label>' + _moleculeSingularTerm + ' was saved</label>').fadeIn(500);

                            _saveMoleculeButtonFader = setTimeout(function () { $('#profilesave-container').fadeOut(1000); }, 2000);

                        }, 500);
                    }

                } else {
                    $.log('ServerUnhappy:' + data.message);
                    RemoveMoleculeExitDialog();
                }
            }
            , error: function (jqXHR, textStatus, errorThrown) {
                alert('Save failed, check console for error message:' +textStatus+' '+ errorThrown);
                MarkMoleculeAsDirty();
                $.log('Molecule Save Error:' + helper_objectToString(textStatus+' '+errorThrown));
            }
        });

where _moleculefilestream is the large base64-encoded stream.

My web.config includes the following:

<system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization maxJsonLength="50000000">
        </jsonSerialization>
      </webServices>
    </scripting>
  </system.web.extensions>

Anyone got any bright ideas?

like image 690
Journeyman Avatar asked Oct 09 '22 15:10

Journeyman


2 Answers

UPDATE

The aspnet:MaxJsonDeserializerMembers element must be added to the <appSettings> section of your web.config file, setting to 2147483647 which is the equivalent of the maximum value of an Int32 data type.

<configuration>
  <appSettings>
    <add key="aspnet:MaxJsonDeserializerMembers" value="2147483647" />
  </appSettings>
</configuration>

In this page of the official documentation, you will be able to find all the information about the elements as well as their use.

Note: In the section of the official documentation it is recommended not to adjust to the maximum value since it represents a security risk. Ideally, you should check the number of items you want to deserialize and try to estimate based on the maximum json size you are using.

Original post by @Flxtr: Here

OLD

If you want to upload a file, why not try using FormData

For example:

function getDataForm() {

    var data = new FormData();

    var files = fileUploader.get(0).files;
    if (files.length > 0) {
        data.append("File", files[0]);
    }
    data.append("ImagePath", "");

    data.append("Id", ImageId);
    data.append("Name", txtName.val().trim());
    data.append("Description", txtDescription.val().trim());

    return data;
}

function save(data) {
    $.ajax({
        type: "POST",
        url: "/Files/SaveImage",
        contentType: false,
        processData: false,
        data: data,
        success: function (response) {

            if (response.success) {
                $.showMessage(messages.NAME, messages.SUCCESS, "success");
                closeForm();
                Files.ImageList.gridImages.ajax.reload();
            }
            else {
                $.showMessage(messages.NAME, response.message, "error");
            };

            btnSave.button('reset');
        },
        error: function (request, status, exception) {
            $.showMessage(messages.NAME, exception, "error");
            btnSave.button('reset');
        }
    });
};

Then, on the server side, change the request length in web config:

<httpRuntime targetFramework="4.6.1" maxRequestLength="65536"/>

For example:

<system.web>
    <compilation debug="true" targetFramework="4.6.1" />
    <httpRuntime targetFramework="4.6.1" maxRequestLength="65536"/>
    <customErrors mode="RemoteOnly">
        <error statusCode="401" redirect="/Error/401" />
        ...
        <error statusCode="411" redirect="/Error/411" />
    </customErrors>
  </system.web>

Also, change the parameter processData to false in the ajax request:

$.ajax({
    url: "/Security/SavePermissions",
    type: "POST",
    processData: false,
    contentType: "application/json; charset=utf-8",
    data: JSON.stringify(pStrPermissions),
    success: function (response) {
        if (response.success) {
            panel.showAlert("Permisos", "Se han actualizado correctamente los permisos.", "success");
            resetForm();
        }
        else {
            $.showMessage("Permisos", response.message, "error");
        };
    },
    error: function (request, status, exception) {
        $.showMessage("Permisos", exception, "error");
    }
});

These are just suggestions. The only thing that actually worked for me was to serialize the list and deserialize it on the server.

For example, on the client side:

function savePermissions(pLstObjPermissions) {
    $.ajax({
        url: "/Security/SavePermissions",
        type: "POST",
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify({ pStrPermissions: JSON.stringify(pLstObjPermissions)}) ,
        success: function (response) {
            if (response.success) {
                panel.showAlert("Permisos", "Se han actualizado correctamente los permisos.", "success");
                resetForm();
            }
            else {
                $.showMessage("Permisos", response.message, "error");
            };
        },
        error: function (request, status, exception) {
            $.showMessage("Permisos", exception, "error");
        }
    });
};

On the server side:

public ActionResult SavePermissions(string pStrPermissions)
{
    var lLstObjResult = new Dictionary<string, object>();

    try
    {
        SecurityFactory.GetPermissionService().UpdateList(JsonConvert.DeserializeObject<IList<Permission>>(pStrPemissions));
        lLstObjResult.Add(MESSAGE, "Registro guardado exitosamente");
        lLstObjResult.Add(SUCCESS, true);
    }
    catch (Exception e)
    {
        lLstObjResult.Add(MESSAGE, e.Message);
        lLstObjResult.Add(SUCCESS, false);
    }

    return Json(lLstObjResult, JsonRequestBehavior.AllowGet);
}

I know that this is not the best way, but it works until something better comes along.

If you have a better way to resolve this problem, please share it.

like image 185
anayarojo Avatar answered Oct 13 '22 11:10

anayarojo


Try setting the httpRuntime's maxRequestLength property.

http://msdn.microsoft.com/en-us/library/e1f13641.aspx

You can set it via a location tag to just the controller/action you need.

like image 43
BNL Avatar answered Oct 13 '22 12:10

BNL