在 Google Apps 脚本中,如何通过重新加载 doPost() 返回的 HTML 来防止多次提交表单数据?
In Google Apps Script, How to prevent multiple submission of form data by reloading the HTML returned by doPost()?
我使用 Google Apps 脚本创建了一个 Web 表单,表单访问者在提交数据后会看到 result.html
。但是,如果访问者通过按 F5、Ctrl + R 重新加载 result.html
,数据可能会提交多次,忽略重新提交的警告。同样的问题已经发布 here, and I tried implementing one of the solutions,但没有用。
我现在在 Google Apps 脚本的同一个项目中有四个文件:
index.html
生成表单
JavaScript.html
定义了 index.html
中使用的函数
result.html
表单提交后显示
code.gs
由 doGet()
显示表单,并处理提交的数据并由 doPost()
呈现 result.html
。此文件中定义的 include()
允许将 JavaScript.html
输入到 index.html
我尝试过的解决方案是添加以下 JavaScript 代码 result.html
。我还将其添加到 JavaScript.html
,这样代码也将在 index.html
中执行。
<script>
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
但是,即使我将该代码添加到 result.html
和 index.html
之后,重新加载 result.html
时仍然会重新提交。我错过了什么?
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<!-- <?!= include("css"); ?> -->
</head>
<body onload="addOptions()"> <!--Execute addOptions function immediately after a page has been loaded-->
<form class="" action="<?!= getScriptUrl(); ?>" method="post" onSubmit="document.getElementById('submit').disabled=true;">
<div>
<h1 id="Question">
Choose either cheesecake or chocolate cake.
</h1>
<select id="dropdownList" name="cake" class="form-control">
</select>
</div>
<div class="form-submit">
<input type="submit" name="" value="Submit">
</div>
</form>
</body>
<?!= include('JavaScript') ?>
</html>
JavaScript.html
<script>
function addOptions() {
/*This will call server-side Apps Script function getAvailableExps and if it is successful,
it will pass the return value to function addListValues which will add options to the drop down menu*/
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(addListValues)
.getAvailableExps();
}
function addListValues(values) {
//Add options to drop down menu using the values of parameter 'values'.
for (var i = 0; i < values.length; i++) {
var option = document.createElement("option");
option.text = values[i][0];
option.value = values[i][0];
var select = document.getElementById("dropdownList");
select.appendChild(option);
}
}
function onFailure(err) {
alert('Error: ' + err.message);
}
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
result.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<base />
<title>Thank you for your order!</title>
<!-- <?!= include('css'); ?> -->
</head>
<script>
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
<body>
<p>
Don't forget what you've ordered!
</p>
</body>
</html>
code.gs
var sheetID = "............................................";
var inventory_sheet = "Inventory";
function doGet(){
return HtmlService.createTemplateFromFile("index").evaluate();
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getScriptUrl() {
var url = ScriptApp.getService().getUrl();
Logger.log(url);
return url;
}
function doPost(e){
var ss = SpreadsheetApp.openById(sheetID);
var sh = ss.getSheets()[0];
sh.appendRow([String(e.parameters.cake)]);
//update Inventory
var inventory = ss.getSheetByName(inventory_sheet);
var row = inventory.createTextFinder(e.parameters.cake).findNext().getRow();
var range = inventory.getRange(row, 2);
var data = range.getValue();
range.setValue(parseInt(data - 1))
return HtmlService.createTemplateFromFile("result").evaluate();
}
function getAvailableExps(){
var inventory = SpreadsheetApp.openById(sheetID).getSheetByName(inventory_sheet);
var data = inventory.getRange(2, 1, 2, 2).getValues();
var filtered = data.filter(arr => arr[1] > 0 || arr[1] != ''); //remove exp to array if quantity is 0 or empty
return filtered;
}
在您的情况下,如何使用 PropertiesService 检查提交?当你的脚本被修改后,变成如下。
修改后的脚本:
本次修改修改了doGet
和code.gs
的doPost
2个函数
doGet
function doGet() {
PropertiesService.getScriptProperties().setProperty("key", "sample");
return HtmlService.createTemplateFromFile("index").evaluate();
}
doPost
function doPost(e) {
var p = PropertiesService.getScriptProperties();
if (p.getProperty("key") == "sample") {
var ss = SpreadsheetApp.openById(sheetID);
var sh = ss.getSheets()[0];
sh.appendRow([String(e.parameters.cake)]);
//update Inventory
var inventory = ss.getSheetByName(inventory_sheet);
var row = inventory.createTextFinder(e.parameters.cake).findNext().getRow();
var range = inventory.getRange(row, 2);
var data = range.getValue();
range.setValue(parseInt(data - 1))
p.deleteProperty("key");
}
return HtmlService.createTemplateFromFile("result").evaluate();
}
- 当您访问 Web 应用程序时,
sample
由 setProperty("key", "sample")
存储在 doGet()
中。并且,当提交 HTML 表单时,PropertiesService 在 doPost(e)
中被选中。当sample
存在时,放入数据,清除PropertiesService。这样,即使重新打开提交的页面,PropertiesService 也不存在。这样就可以避免重投了。
参考:
我使用 Google Apps 脚本创建了一个 Web 表单,表单访问者在提交数据后会看到 result.html
。但是,如果访问者通过按 F5、Ctrl + R 重新加载 result.html
,数据可能会提交多次,忽略重新提交的警告。同样的问题已经发布 here, and I tried implementing one of the solutions,但没有用。
我现在在 Google Apps 脚本的同一个项目中有四个文件:
index.html
生成表单JavaScript.html
定义了index.html
中使用的函数
result.html
表单提交后显示code.gs
由doGet()
显示表单,并处理提交的数据并由doPost()
呈现result.html
。此文件中定义的include()
允许将JavaScript.html
输入到index.html
我尝试过的解决方案是添加以下 JavaScript 代码 result.html
。我还将其添加到 JavaScript.html
,这样代码也将在 index.html
中执行。
<script>
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
但是,即使我将该代码添加到 result.html
和 index.html
之后,重新加载 result.html
时仍然会重新提交。我错过了什么?
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<!-- <?!= include("css"); ?> -->
</head>
<body onload="addOptions()"> <!--Execute addOptions function immediately after a page has been loaded-->
<form class="" action="<?!= getScriptUrl(); ?>" method="post" onSubmit="document.getElementById('submit').disabled=true;">
<div>
<h1 id="Question">
Choose either cheesecake or chocolate cake.
</h1>
<select id="dropdownList" name="cake" class="form-control">
</select>
</div>
<div class="form-submit">
<input type="submit" name="" value="Submit">
</div>
</form>
</body>
<?!= include('JavaScript') ?>
</html>
JavaScript.html
<script>
function addOptions() {
/*This will call server-side Apps Script function getAvailableExps and if it is successful,
it will pass the return value to function addListValues which will add options to the drop down menu*/
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(addListValues)
.getAvailableExps();
}
function addListValues(values) {
//Add options to drop down menu using the values of parameter 'values'.
for (var i = 0; i < values.length; i++) {
var option = document.createElement("option");
option.text = values[i][0];
option.value = values[i][0];
var select = document.getElementById("dropdownList");
select.appendChild(option);
}
}
function onFailure(err) {
alert('Error: ' + err.message);
}
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
result.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<base />
<title>Thank you for your order!</title>
<!-- <?!= include('css'); ?> -->
</head>
<script>
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
<body>
<p>
Don't forget what you've ordered!
</p>
</body>
</html>
code.gs
var sheetID = "............................................";
var inventory_sheet = "Inventory";
function doGet(){
return HtmlService.createTemplateFromFile("index").evaluate();
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getScriptUrl() {
var url = ScriptApp.getService().getUrl();
Logger.log(url);
return url;
}
function doPost(e){
var ss = SpreadsheetApp.openById(sheetID);
var sh = ss.getSheets()[0];
sh.appendRow([String(e.parameters.cake)]);
//update Inventory
var inventory = ss.getSheetByName(inventory_sheet);
var row = inventory.createTextFinder(e.parameters.cake).findNext().getRow();
var range = inventory.getRange(row, 2);
var data = range.getValue();
range.setValue(parseInt(data - 1))
return HtmlService.createTemplateFromFile("result").evaluate();
}
function getAvailableExps(){
var inventory = SpreadsheetApp.openById(sheetID).getSheetByName(inventory_sheet);
var data = inventory.getRange(2, 1, 2, 2).getValues();
var filtered = data.filter(arr => arr[1] > 0 || arr[1] != ''); //remove exp to array if quantity is 0 or empty
return filtered;
}
在您的情况下,如何使用 PropertiesService 检查提交?当你的脚本被修改后,变成如下。
修改后的脚本:
本次修改修改了doGet
和code.gs
的doPost
2个函数
doGet
function doGet() {
PropertiesService.getScriptProperties().setProperty("key", "sample");
return HtmlService.createTemplateFromFile("index").evaluate();
}
doPost
function doPost(e) {
var p = PropertiesService.getScriptProperties();
if (p.getProperty("key") == "sample") {
var ss = SpreadsheetApp.openById(sheetID);
var sh = ss.getSheets()[0];
sh.appendRow([String(e.parameters.cake)]);
//update Inventory
var inventory = ss.getSheetByName(inventory_sheet);
var row = inventory.createTextFinder(e.parameters.cake).findNext().getRow();
var range = inventory.getRange(row, 2);
var data = range.getValue();
range.setValue(parseInt(data - 1))
p.deleteProperty("key");
}
return HtmlService.createTemplateFromFile("result").evaluate();
}
- 当您访问 Web 应用程序时,
sample
由setProperty("key", "sample")
存储在doGet()
中。并且,当提交 HTML 表单时,PropertiesService 在doPost(e)
中被选中。当sample
存在时,放入数据,清除PropertiesService。这样,即使重新打开提交的页面,PropertiesService 也不存在。这样就可以避免重投了。