Brian Ogden
Brian Ogden

Reputation: 19212

asmx Web Service failing to return JSON with FineUploader file upload

I know there are many questions similar to mine out there on this subject but those don't solve my issue. I know that web services naturally parse my objects into json as part of the framework. I have manually set the request header header Accept to 'application/json, text/javascript, /; q=0.01'. I have added the <ScriptMethod(ResponseFormat:=ResponseFormat.Json)> to my web service.

It is clear that my web service is responding to the file upload request header with a text/plain response and parsing my simple FineUploaderResponse object is failing. Keep in mind that a regular jQuery AJAX call to the same web service works fine. I would prefer not to use Web API or generic handlers in place of my web as multiple websites are reliant on my framework and expect this standard.

Thanks in advance!

The code:

Public Class FineUploaderResponse
        Property Success As Boolean
    End Class

 <WebMethod(EnableSession:=True)> _
<ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
Public Function UploadPhotos()
    'next three lines are pointless, didn't help
    'HttpContext.Current.Response.Clear()
    'HttpContext.Current.Response.ContentType = "application/json"
    'HttpContext.Current.Response.Charset = "utf-8"

    Dim response As New FineUploaderResponse()
    response.Success = True

    Return response

End Function

Now of course this web service works if I do this:

  $(document).ready(function () {
            $.ajax(
            {
                url: "/Services/PhotosService.asmx/UploadPhotos",
                type: "POST",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (data) {
                    console.log(data.d);
                }

            });

        });

The standard jquery request header is

POST http://localhost:3066/Services/PhotosService.asmx/UploadPhotos HTTP/1.1
Host: localhost:3066
Proxy-Connection: keep-alive
Content-Length: 0
Cache-Control: no-cache
Pragma: no-cache
Origin: http://localhost:3066
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
Content-Type: application/json; charset=utf-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Referer: http://localhost:3066/Test.aspx
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

But I am using FineUploader and it is posting a Content-Type:multipart/form-data; Notice though the Accept header is the same for both jQuery AJAX and FineUploader requests:

POST http://localhost:3066/Services/PhotosService.asmx/UploadPhotos HTTP/1.1
Host: localhost:3066
Proxy-Connection: keep-alive
Content-Length: 110634
Cache-Control: no-cache
Pragma: no-cache
Origin: http://localhost:3066
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary449tHPTKEpuO5jOR
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Referer: http://localhost:3066/Sellers/photos/?pid=37344
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

The web service response from the standard Jquery ajax call is:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 17 Apr 2013 00:12:21 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private, max-age=0
Content-Type: application/json; charset=utf-8
Content-Length: 22
Connection: Close

The web service response from a FineUploader post file request is:

HTTP/1.1 500 Internal Server Error
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 17 Apr 2013 00:18:26 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Content-Length: 1936
Connection: Close

The internal server 500 error error message details are:

System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type Kazork.AppCode.PhotosService+FineUploaderResponse was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
   at System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(String name, String ns, Object o, Boolean xsiType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write1_Object(String n, String ns, Object o, Boolean isNullable, Boolean needType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write3_anyType(Object o)
   at Microsoft.Xml.Serialization.GeneratedAssembly.ObjectSerializer1.Serialize(Object objectToSerialize, XmlSerializationWriter writer)
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   at System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, Object o, XmlSerializerNamespaces namespaces)
   at System.Web.Services.Protocols.XmlReturnWriter.Write(HttpResponse response, Stream outputStream, Object returnValue)
   at System.Web.Services.Protocols.HttpServerProtocol.WriteReturns(Object[] returnValues, Stream outputStream)
   at System.Web.Services.Protocols.WebServiceHandler.WriteReturns(Object[] returnValues)
   at System.Web.Services.Protocols.WebServiceHandler.Invoke()

And, in case you are curious, here is my FineUploader call:

 var uploader = new qq.FineUploader({
            element: document.getElementById('bootstrapped-fine-uploader'),
            request: {
                endpoint: "/Services/PhotosService.asmx/UploadPhotos",
                forceMultipart: true,
                params: { propertyId:<%=PropertyId %>},
                customHeaders: { Accept: 'application/json, text/javascript, */*; q=0.01' }
            },
            text: {
                uploadButton: '<i class="icon-upload icon-white"></i>Upload nice images.'
            },
            template: '<div class="qq-uploader">' +
                        '<pre class="qq-upload-drop-area"><span>{dragZoneText}</span></pre>' +
                        '<div class="qq-upload-button btn btn-success" style="width: auto;">{uploadButtonText}</div>' +
                        '<span class="qq-drop-processing"><span>{dropProcessingText}</span><span class="qq-drop-processing-spinner"></span></span>' +
                      '</div>' +
                      '<ul class="qq-upload-list" style="margin-top: 10px; text-align: center;"></ul>' +
                      '',
            classes: {
                success: 'alert alert-success',
                fail: 'alert alert-error'
            },
            debug: false,
            callbacks: {
//                onComplete: function (id, fileName, responseJson) {
//                    $.when(loadThumbs()).done(function () {
//                        $(".qq-upload-list > .alert-success").remove();
//                    });
//                    toastr.success("Success!");
//                },
                 onComplete: function(id, fileName, responseJSON) {
                      if (responseJSON.success) {
                        $('#file-' + id).removeClass('alert-info')
                                        .addClass('alert-success')
                                        .html('<i class="icon-ok"></i> ' +
                                              'Successfully saved ' +
                                              '“' + fileName + '”' +
                                              '<br><img src="/images/message_ok.png" alt="' + fileName + '">');

                          $.when(loadThumbs()).done(function () {
                                $(".qq-upload-list > .alert-success").remove();
                            });
                          toastr.success("Success!");
                      } else {
                        $('#file-' + id).removeClass('alert-info')
                                        .addClass('alert-error')
                                        .html('<i class="icon-exclamation-sign"></i> ' +
                                              'Error with ' +
                                              '“' + fileName + '”: ' +
                                              responseJSON.error);
                      }
                },
                onError: function (id, fileName, errorReason) {
                    toastr.error("Failed! Try again.");
                }
            }
        });

Upvotes: 1

Views: 1853

Answers (1)

Brian Ogden
Brian Ogden

Reputation: 19212

Ok easy fix, frustrating though, web services kill a lot of my time trying to maintain a contemporary web app. But I don't have a need to migrate to Web API at this point, primarily because my web app is reliant on session (you can introduce session into Web API but that is not RESTful obviously).

So I replaced the FineUploaderResponse class with my own formatted JSON response:

<WebMethod(EnableSession:=True)> _
<ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
Public Sub UploadPhotos()

    Context.Response.Write(New ResultData().GetResultDataJSON("success", "true"))

End Sub


    Public Function GetResultDataJSON(key As String, value As String) As String
        Dim oBuilder As StringBuilder = New StringBuilder()
        oBuilder.Append("{")
        oBuilder.AppendFormat("""{0}"" : {1}", key, value)
        oBuilder.Append("}")
        Return oBuilder.ToString()
    End Function

And here is my FineUploader Javascript:

/*=================================================*/
//fineUploaderInitialize
/*=================================================*/
    function createUploader() {
        var uploader = new qq.FineUploader({
            element: document.getElementById('bootstrapped-fine-uploader'),
            request: {
                endpoint: "/Services/PhotosService.asmx/UploadPhotos",
                forceMultipart: true,
                params: { propertyId:$('#hiddenPropertyIdUploadPhotosUserControl').val()},
                customHeaders: { Accept: 'application/json, text/javascript, */*; q=0.01' },
                allowedExtensions: ['gif', 'jpeg', 'jpg', 'png'],
            },
            text: {
                uploadButton: 'Drag & drop photos into this area or CLICK HERE to upload photos'
            },
            template: '<div class="qq-uploader text-center" style="height:70px;background-color:white;border-radius:6px;">' +
                        '<div class="qq-upload-button btn btn-success col-lg-12">{uploadButtonText}</div>' +
                        '<span class="qq-drop-processing"><span>{dropProcessingText}</span><span class="qq-drop-processing-spinner"></span></span>' +
                        '<pre class="qq-upload-drop-area"><span>{dragZoneText}</span></pre>' +
                      '</div>' +
                      '<ul class="qq-upload-list" style="margin-top: 10px; text-align: center;"></ul>' +
                      '',
            classes: {
                success: 'alert alert-success',
                fail: 'alert alert-error'
            },
            debug: false,
            callbacks: {
                onComplete: function(id, fileName, responseJSON) {
                    if (responseJSON.success) {
                        $('#file-' + id).removeClass('alert-info')
                                        .addClass('alert-success')
                                        .html('<i class="glyphicon glyphicon-ok"></i> ' +
                                              'Successfully saved ' +
                                              '“' + fileName + '”' +
                                              '<br><img src="/images/message_ok.png" alt="' + fileName + '">');
                        $.when(loadThumbs()).done(function () {
                            $(".qq-upload-list > .alert-success").remove();
                        });
                        toastr.success("Success!");
                    } else {
                        $('#file-' + id).removeClass('alert-info')
                                        .addClass('alert-error')
                                        .html('<i class="glyphicon glyphicon-exclamation-sign"></i> ' +
                                              'Error with ' +
                                              '“' + fileName + '”: ' +
                                              responseJSON.error);
                    }
                },
                onError: function (id, fileName, errorReason) {
                    if(errorReason == 'XHR returned response code 0'){
                        toastr.error('File Size Cannot Exceed 20 Megabytes');
                    }else{
                        toastr.error(errorReason);
                    }
                }
            }
        });
    }
/*=================================================*/

Upvotes: 1

Related Questions