我正在开发Grails Web应用程序,该应用程序的模块主要必须实现主从接口。作为一种方法,我想考虑以下代码:
大师班:
import org.codehaus.groovy.grails.web.json.JSONArray
class MyForm {
String name
String address
String detail
BigDecimal total
static hasMany = [details: MyFormDetail]
static constraints = {
name()
address()
detail()
total()
}
static mapping = {
detail type: 'text'
}
def beforeInsert = {
def detailJSON = new JSONArray(detail)
detailJSON.each {
def quantity = it.getAt("quantity").toString().toBigDecimal()
def description = it.getAt("description").toString()
def unitaryPrice = it.getAt("unitaryPrice").toString().toBigDecimal()
def subtotal = it.getAt("subtotal").toString().toBigDecimal()
def myFormDetail = new MyFormDetail(
quantity: quantity,
description: description,
unitaryPrice: unitaryPrice,
subtotal: subtotal
)
this.addToDetails(myFormDetail)
}
}
}
详细类别:
class MyFormDetail {
Integer quantity
String description
BigDecimal unitaryPrice
BigDecimal subtotal
static belongsTo = [myForm: MyForm]
static constraints = {
quantity()
description()
unitaryPrice()
subtotal()
}
}
myFormUtilities.js帮助文件:
$(document).ready(function() {
$("#detailTable").jqGrid({
datatype: "local",
height: 100,
colNames: ["QUANTITY","DESCRIPTION","UNIT. PRICE","SUBTOTAL"],
colModel:[
{name:'quantity',index:'quantity', width:100},
{name:'description',index:'description', width:400},
{name:'unitaryPrice',index:'unitaryPrice', width:100},
{name:'subtotal',index:'subtotal', width:100}],
caption: "DETAIL"
});
createTable();
$("#addRow").bind("click",addRow);
$("#removeRow").bind("click",removeRow);
$("#quantity, #unitaryPrice").bind("keyup",calculateTotal);
function calculateTotal(){
let quantity = parseFloat($("#quantity").val());
let unitaryPrice = parseFloat($("#unitaryPrice").val());
let subtotal = quantity*unitaryPrice;
$("#subtotal").val(subtotal);
}
function addRow(){
let row = new Object();
row.quantity = $("#quantity").val();
row.description = $("#description").val();
row.unitaryPrice = $("#unitaryPrice").val();
row.subtotal = $("#subtotal").val();
let detailJSON = ($("#detail").val()=="")?"[]":$("#detail").val();
let mydata = $.parseJSON(detailJSON);
mydata.push(row);
$("#detail").val(JSON.stringify(mydata));
createTable();
}
function removeRow(){
let filaId = parseInt($('#detailTable').jqGrid('getGridParam','selrow')) - 1;
let mydata = $.parseJSON($("#detail").val());
let nuevoMydata = new Array();
for(let i=0;i<mydata.length;i++){
if(filaId!=i)
nuevoMydata.push(mydata[i]);
}
$("#detail").val(JSON.stringify(nuevoMydata));
createTable();
}
function createTable(){
let total = 0;
let aRow = new Object();
let detailJSON = ($("#detail").val()=="")?"[]":$("#detail").val();
let mydata = $.parseJSON(detailJSON);
$("#detailTable").jqGrid("clearGridData", true);
for(let i=0;i<mydata.length;i++){
aRow = mydata[i];
total += parseFloat(aRow.subtotal);
$("#detailTable").jqGrid('addRowData',i+1,aRow);
}
$("#total").val(total);
}
});
这是显示的表单(我知道这是一个自动生成的视图,但是请看成一个非常基本的GUI样机):
所以,这些是问题:
Subtotal
和Total
字段都是已设置的计算字段read-only
阻止用户修改其内容,但我发现使用浏览器元素检查器的内容和属性(如只读)可以修改。
detail
同样发生。如果其内容被更改,则在服务器端保存实例时将产生错误,因为beforeInsert
块需要有效用于创建detail
实例的JSON字符串。此字段还用于生成详细信息JqGrid。该字段将是隐藏。
此示例的所有操作均按预期工作,但好奇的用户可能会导致故障。
detail
字段来保存JSON字符串,这是实现主从模式的解决方案。还有另一个改进此实现方式的方法,无需定义此字符串的字段?如果您的用户有权访问检查器/ Web控制台,则他们有权访问您执行的所有客户端数据(它们仅受其如何使用浏览器开发工具的知识所限制)。他们可以更改任何元素的文本或输入的值。因此,唯一可以100%安全地存储此数据的位置是在服务器端。
[您可以尝试做的是,至少隐藏每个输入(例如,使用CSS)并将其替换为可见,这至少会使他们更新任何东西,并可能阻止更改事件调用服务器,具有<span>
元素(或其他一些非格式元素)的HTML。然后确保无论何时更改<input>
值,都将更新相应跨度的文本。类似于:
$("#subtotalSpan").html( $("#subtotal").val() );
添加在calculateTotal函数内部。如果您希望他们甚至无法编辑跨度的HTML,则可以走得更远,并将值绘制到<canvas>
而不是<span>
。他们将无法更改绘制到canvas元素中的文本。
[抱歉,对于保护从浏览器发送的数据没有简单的答案。您的服务器应将任何用户提交的输入视为脏内容。
在POST或GET中发送的数据始终可以在客户端更改,甚至可以批量创建(使用CURL或Postman之类的东西)。因此,您应该始终在服务器端验证用户输入并在此计算您的总数。