最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Catch too long form submission - Stack Overflow

programmeradmin6浏览0评论

I am developing an HTML page which has simple HTML form (nothing special) being submitted by button. There is a couple of situations when form submitted and response comes too long (if whenever really comes back). How can i organize the form the way it fires some callback when waiting a response is too long? We could show up some notice for user, indicating our server is overloaded in that situation.

Here is request that being sent by form:

POST  HTTP/1.1
Host: example
Proxy-Connection: keep-alive
Content-Length: 83
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: 
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: /
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: [cookie definition omitted]

[form data omitted]

Is Proxy-Connection: keep-alive influence the process somehow? Googling led me to plugin, but it is for a little bit different purpose.

I am developing an HTML page which has simple HTML form (nothing special) being submitted by button. There is a couple of situations when form submitted and response comes too long (if whenever really comes back). How can i organize the form the way it fires some callback when waiting a response is too long? We could show up some notice for user, indicating our server is overloaded in that situation.

Here is request that being sent by form:

POST http://example.com/search HTTP/1.1
Host: example.com
Proxy-Connection: keep-alive
Content-Length: 83
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://example.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://example.com/
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: [cookie definition omitted]

[form data omitted]

Is Proxy-Connection: keep-alive influence the process somehow? Googling led me to https://github.com/caxap/jquery-safeform plugin, but it is for a little bit different purpose.

Share Improve this question edited Oct 5, 2015 at 12:39 kseen asked Oct 5, 2015 at 12:23 kseenkseen 3979 gold badges58 silver badges107 bronze badges 12
  • Instead of normal form submit try an Ajax submit, that way you can have a setTimeout function for show the message you want to show. – Vivek Commented Oct 5, 2015 at 12:26
  • @Vivek Thanks for quick response! I have thought about such way, but how can I make it acts like a regular form submission then? When AJAXing, I should manually handle the response. I think there is much easier way. – kseen Commented Oct 5, 2015 at 12:28
  • The request timeout can often be enforced server-side. – plalx Commented Oct 5, 2015 at 12:29
  • @plalx Thank you! Seems to be very closer to the way I expecting. Could you please describe it? – kseen Commented Oct 5, 2015 at 12:30
  • I do not know ASP.Net very well, but perhaps it could be this msdn.microsoft.com/en-us/library/… – plalx Commented Oct 5, 2015 at 12:31
 |  Show 7 more comments

5 Answers 5

Reset to default 13 +25

It depends on what type of UI you want to present to the user. You can simply lower the timeout at the server level and if it can't finish the response in time, it will abort. However, the user experience is pretty harsh, as they'll just get a generic timeout error that likely won't even be from your site. (They'll have to click back or something to get back to your site.)

If you just want to display a message after a certain amount of time has passed, you can attach to the form's submit event and use setTimeout to display an alert or something:

$('#MyForm').on('submit', function () {
    setTimeout(30000, function () { // number is milliseconds
        alert('Sorry this is taking so long.');
    });
});

Finally, if there's some method of tracking the progress of the action that's being completed server-side, you could use something like web sockets or long-polling via AJAX to provide a progress bar or status update of some sort. That's a bit more complex, though, and will require some research on your part.

There are two approaches, I will write two separate answers.

XMLHttpRequest progress approach (for modern browsers)

Just send data and read uploading progress from XMLHttpRequest:

//-------------upload ------------------


var lastProgressIndex = -1;


//is the file api available in this browser
//only override *if* available.
if (new XMLHttpRequest().upload) {
    $("#upload-files").click(function () {       
        upload_files($("#files-to-upload")[0].files, lastProgressIndex++);
        return false;
    });
    $("#files-to-upload").change(function () {
        upload_files($("#files-to-upload")[0].files, lastProgressIndex++);
        return false;
    });
    $("#upload-files").hide();
}
function resetFormElement(elem) {
    elem.wrap('<form>').closest('form').get(0).reset();
    elem.unwrap();
}

function clear_upload() {
    //https://stackoverflow.com/questions/1043957/clearing-input-type-file-using-jquery
    var upload = $("#files-to-upload");
    //upload.replaceWith(upload = upload.clone(true));
    resetFormElement(upload);
}

//accepts the input.files parameter
function upload_files(filelist) {
    if (typeof filelist !== "undefined") {
        for (var i = 0, l = filelist.length; i < l; i++) {
            upload_file(filelist[i], lastProgressIndex++);
        }
    }
    clear_upload();
}

//each file upload produces a unique POST
function upload_file(file, index) {

    //TODO - vytvor progress bar podle indexu
    $("#progresscontainer").append('<div id="progressbar' + index + '" class="progressbar"><div id="progresslabel' + index + '" class="progressbarlabel"></div></div>')

    var progressBarSelector = "#progressbar" + index;
    var progressLabelSelector = "#progresslabel" + index;
    var fileName = file.name;

    var xhr = new XMLHttpRequest();

    xhr.upload.addEventListener("progress", function (evt) {
        if (evt.lengthComputable) {
            //update the progress bar

            var percent = Math.floor((evt.loaded / evt.total) * 100) + "%";

            //TODO http://www.binaryintellect.net/articles/859d32c8-945d-4e5d-8c89-775388598f62.aspx                  

            $(progressBarSelector).css({
                width: percent
            });
            $(progressLabelSelector).html(fileName + ' ' + percent);
        }
    }, false);

    // File uploaded
    xhr.addEventListener("load", function () {
        $(progressLabelSelector).html(fileName + " uploaded");

        AddImageToGallery(GetFilenameWithoutExt(fileName));

        $(progressBarSelector).fadeOut(500, function () {
            $(progressBarSelector).remove();
        });
    }, false);

    var guid = $("#Identification").val();
    xhr.open("post", "/uploadurl/uploadfile/" + guid, true);

    // Set appropriate headers
    // We're going to use these in the UploadFile method
    // To determine what is being uploaded.
    xhr.setRequestHeader("Content-Type", "multipart/form-data");
    xhr.setRequestHeader("X-File-Name", file.name);
    xhr.setRequestHeader("X-File-Size", file.size);
    xhr.setRequestHeader("X-File-Type", file.type);

    // Send the file
    xhr.send(file);
}

And server part:

private UploadedFile[] RetrieveFileFromRequest()
{
    List<UploadedFile> uploadedFiles = new List<UploadedFile>();


    if (Request.Files.Count > 0)
    { //they're uploading the old way

        for (int i = 0; i < Request.Files.Count; i++)
        {
            var file = Request.Files[0];
            string contentType = file.ContentType;
            string filename = file.FileName;

            UploadedFile uploadedFile = SaveUploadedFile(file.InputStream, file.ContentLength, filename, contentType);

            uploadedFiles.Add(uploadedFile);
        }
    }
    else if (Request.ContentLength > 0)
    {
        string filename = Request.Headers["X-File-Name"];
        string contentType = Request.Headers["X-File-Type"];

        UploadedFile uploadedFile = SaveUploadedFile(Request.InputStream, Request.ContentLength, filename, contentType);
        uploadedFiles.Add(uploadedFile);
    }

    return uploadedFiles.ToArray();
}

These sources are modification of the original article. There is related stackoverflow question.

How can I organize the form the way it fires some callback when waiting a response is too long?

This is based on your algorithm. You can estimate the time like brute force calculation and show the result before beginning to perform. It's not an optimized solution if you start processing and then break it when too many time is spent!

Update: If you can't estimate like above, At least, write an asynch controller method which is useful when an action must perform several independent long running operations. Follow this:

  1. Write an Ajax Form with unobtrusive-ajax
  2. Define a function for OnBegin form option to call a timer
  3. The timer's time out event will show another thing to user.
  4. Write an asynch controller method for the operation which contains a timer to stop processing when it takes more than ?? seconds.

There are two approaches, I will write two separate answers.

Sending thread/UI thread approach

You will need 2 javascript "threads".

Sending thread: With first open websocket and start sending data to server. On the server side update some variable how many data was sent and how many remaining.

UI thread: Using Ajax ask server frequently (HTTP request with JSON format is fine), how many data was send and how many remaining and render the progressbar in javascript.

Finnaly, when UI thread request receive information that upload is ready, remove progressbar with jquery and show message or redirect page.

With this approach you can easily show send bytes, estimation time remaining, upload speed.

So every javascript thread has corresponding server tread. And both server threads share the structure with dataSend and dataRemaining dependent on your server technology.

Bellow You can find part of my code which upload several images and show preview after finish is finished:

intervalID = window.setInterval(function() 
{
    var request = new Sys.Net.WebRequest();
    request.set_url('UploadProgress.ashx?DJUploadStatus=' + token + '&ts=' + new Date().getTime());
    request.set_httpVerb('GET');
    request.add_completed(function(executor) {

        //  the progress is returned as xml
        var e = executor.get_xml().documentElement;
        var empty = e.getAttribute('empty');

        if (!empty) 
        {
            //  extract the attributes I am interested in
            var percent = e.getAttribute('progress');
            var file = e.getAttribute('file');
            var kbs = Math.round(parseInt(e.getAttribute('bytes')) / 1024);
            var size = Math.round(parseInt(e.getAttribute('size')) / 1024);

            //  update the progressbar to the new value
            progressBar.set_percentage(percent);

            //  upadte the message
            updateMessage('info', 'Uploading ' + file + ' ... ' + kbs + ' of ' + size + ' KB');

            if (percent == 100)
            {
                //  clear the interval
                window.clearInterval(intervalID);
            }
        }
    });

    //  invoke the request
    request.invoke();

}, 1000);

You could check for WebTimeout exception from server side then use SignalR to actively send timeout messsage back to client side:

var request = (HttpWebRequest)WebRequest.Create("http://www.your_third_parties_page.com");
request.Timeout = 1000; //Timeout after 1000 ms

try
{
    using (var stream = request.GetResponse().GetResponseStream())
    using (var reader = new StreamReader(stream))
    {
         Console.WriteLine(reader.ReadToEnd());
    }
}
catch (WebException ex)
{
    //Check for timeout exception
    if (ex.Status == WebExceptionStatus.Timeout)
    {
         //If timeout then send SignalR ajax message to inform the clients...
         var context = GlobalHost.ConnectionManager.GetHubContext<YourHub>();
         context.Clients.All.addNewMessageToPage("This method has been processing too long!");
    }
}

See more how to setup SignalR for asp.net here

发布评论

评论列表(0)

  1. 暂无评论