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

In Javascript how can I redirect an Ajax response in window location - Stack Overflow

programmeradmin0浏览0评论

I currently have the following working piece of code (angular but applies to any JS framework):

var url = '/endpoint/to/my/file';

$http({
  method: 'GET',
  url: url
})
.success(function(jdata) {
  window.location = url;
})
.error(function(je){
  // display errors on page
});

The above is called after a form was pleted and the user has clicked on "submit" (the real situation is a bit more plex than this but it is the same idea). I do the form check asynchronously, so there's no page reload.

If the request is successful, returns a binary (a pdf file), if not succesful, the request returns a 400 BadRequest with errors formatted in JS. So what I do is, if successful, I redirect to the same url to have the PDF otherwise I get the JSON error object and do something with it.

How can I refrain from making two requests if the requests is successful?

  • Note1: on the backend side I would like to keep only one route that does everything, check + return PDF
  • Note2: the current situation is pretty neat in my opinion, since I have an asynchronous form check and if successful the file downloads directly in the browser since I have "CONTENT-DISPOSITION" -> "attachment" in the HTTP header of the successful response

Update: additional information about the architecture as requested by Emile: In my use case I have one endpoint that checks inputs (and other external requirements). For security reasons I cannot output the PDF if all requirements are not satisfied so I have to do the check prior to delivering the file ( the file is automatically generated) anyway. So having two endpoints would just be redundant and add some unnecessary plexity.

While writing I think an alternative solution could be to pass an argument on the endpoint while doing the check, so that if successful, it stops and does not generate the PDF, and then redirect to the same endpoint without the flag which will output the PDF. So I do the check twice but only load (and generate - which is resource intensive) the file only once and I have only one endpoint...

Here's the adapted code:

var url = '/endpoint/to/my/file';

$http({
  method: 'GET',
  url: url+'?check'
})
.success(function(jdata) {
  window.location = url;
})
.error(function(je){
  // display errors on page
});

On the backend side (I use Play framework/Scala)

def myendpoint(onlyDoCheck: Boolean = false) = Action{implicit request =>
   myForm.bindFromRequest.fold(
     e => BadRequest(myErrors),
     v => if(onlyDoCheck) Ok(simpleOkResponse) else  Ok(doComputationgeneratefileandoutputfile)
   )
}

I currently have the following working piece of code (angular but applies to any JS framework):

var url = '/endpoint/to/my/file';

$http({
  method: 'GET',
  url: url
})
.success(function(jdata) {
  window.location = url;
})
.error(function(je){
  // display errors on page
});

The above is called after a form was pleted and the user has clicked on "submit" (the real situation is a bit more plex than this but it is the same idea). I do the form check asynchronously, so there's no page reload.

If the request is successful, returns a binary (a pdf file), if not succesful, the request returns a 400 BadRequest with errors formatted in JS. So what I do is, if successful, I redirect to the same url to have the PDF otherwise I get the JSON error object and do something with it.

How can I refrain from making two requests if the requests is successful?

  • Note1: on the backend side I would like to keep only one route that does everything, check + return PDF
  • Note2: the current situation is pretty neat in my opinion, since I have an asynchronous form check and if successful the file downloads directly in the browser since I have "CONTENT-DISPOSITION" -> "attachment" in the HTTP header of the successful response

Update: additional information about the architecture as requested by Emile: In my use case I have one endpoint that checks inputs (and other external requirements). For security reasons I cannot output the PDF if all requirements are not satisfied so I have to do the check prior to delivering the file ( the file is automatically generated) anyway. So having two endpoints would just be redundant and add some unnecessary plexity.

While writing I think an alternative solution could be to pass an argument on the endpoint while doing the check, so that if successful, it stops and does not generate the PDF, and then redirect to the same endpoint without the flag which will output the PDF. So I do the check twice but only load (and generate - which is resource intensive) the file only once and I have only one endpoint...

Here's the adapted code:

var url = '/endpoint/to/my/file';

$http({
  method: 'GET',
  url: url+'?check'
})
.success(function(jdata) {
  window.location = url;
})
.error(function(je){
  // display errors on page
});

On the backend side (I use Play framework/Scala)

def myendpoint(onlyDoCheck: Boolean = false) = Action{implicit request =>
   myForm.bindFromRequest.fold(
     e => BadRequest(myErrors),
     v => if(onlyDoCheck) Ok(simpleOkResponse) else  Ok(doComputationgeneratefileandoutputfile)
   )
}
Share Improve this question edited Jul 29, 2016 at 20:27 John asked Jul 29, 2016 at 19:08 JohnJohn 5,36410 gold badges39 silver badges47 bronze badges 10
  • 1 have you seen: stackoverflow./questions/20830309/… – Iceman Commented Jul 29, 2016 at 19:14
  • url is in an object closure and methods are chain. I haven't used angular, but could you jump to that location url object with: window.location = this.url; – Leroy Thompson Commented Jul 29, 2016 at 19:16
  • 1 @iceman If I understood you right, yes. I edited the question for more clarity – John Commented Jul 29, 2016 at 19:24
  • 1 See my edited answer to understand how to handle the response – Emile Bergeron Commented Jul 29, 2016 at 19:35
  • 1 @John 's answer using HTML5 download is probably your only soln. Note that Safari, IE, Safari-iOS, Opera-mini and some such browsers dont support this particular spec. – Iceman Commented Jul 29, 2016 at 19:48
 |  Show 5 more ments

1 Answer 1

Reset to default 9

The real deal

The best you could do is split your endpoint.

  1. One for the form and the convenience of having errors without refresh.
  2. Then, on success, redirect to your other endpoint which only downloads the file.

If the file was on the disk and wasn't auto-generated and required to be authenticated to be downloaded, you could hide the file behind a normal endpoint, do the checks, and return the file using X-Accel-Redirect header with nginx or X-Sendfile using apache.

The hack

Disclaimer: This is more of a hack than the best solution. As mention by @Iceman, Safari, IE, Safari-iOS, Opera-mini and some such browsers don't support this particular spec.

In your server-side endpoint, if the file is available without errors, you can set the header to the content-type of the file (like 'application/pdf') so the download will starts automatically.

If there are errors, don't set the header and return a json of the errors to inform the user through javascript.

Since we don't know what's behind, here's a python (Django) example:

response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename=your_filename.pdf'
response.write(repport.printReport(participantId))
return response

You can handle the response in the ajax success callback:

$.ajax({
    url: 'endpoint.php',
    success: function(data) {
        var blob = new Blob([data]);
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = "filename.pdf";
        link.click();
    }
});

You could also try the jQuery fileDownload plugin mentioned in this answer.

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>