记录一次bug解决过程:velocity中获取url中的参数
一、总结
- 在Webx的Velocity中获取url中参数:$rundata.getRequest().getParameter('userId')
- 在Webx项目中,防止CSRF攻击(Cross-site request forgery,跨站请求伪造),在form表单提交中要加入$!csrfToken.ajaxUniqueToken
- 在MyBatis的mapper层,使用标签association实现对象的关联,一个bean配多个association标签。
二、Bug描述:Velocity从URL中获取parameter参数
在项目IDCM中,使用webx容器进行项目的开发。前端的模板引擎采用了velocity,在项目中,当从列表页跳到详情页的时候,通常我们的screen层是采用如下方式进行展现的:
public class EditRules extends BaseScreen {
@Autowired
private AutoAssignSupplierBo autoAssignSupplierBo;
@Autowired
private SupplierBo supplierBo;
@Autowired
private AddressBo addressBo;
@Autowired
private SiteBo siteBo; public void execute(@Param("id")
String id, Context context) throws Exception {
QueryAssignRulesrDo query = new QueryAssignRulesrDo();
if (StringUtils.isBlank(id)) {
throw new ServiceException("id is empty ");
}
query.setRuleId(Long.parseLong(id));
BoResultDTO<List<AssignRulesVo>> result = autoAssignSupplierBo.selectByQuery(query);
List<AssignRulesVo> list = result.getData();
if (CollectionUtils.isNotEmpty(list)) {
// 当前规则的详情
if (StringUtils.isNotBlank(list.get(0).getType())) {
list.get(0).setTypeVal(list.get(0).getType());
list.get(0).setType(WorkOrderCst.RelocationType.getNameByStrValue(list.get(0).getType()));
}
// 如果有数量信息需要展示
if (list.get(0).getRuleContent().contains("数量")) {
String[] numDes = list.get(0).getRuleContent().split(" ");
for (String str : numDes) {
if (str.contains("数量")) {
String[] sz = str.split(":");
if (2 == sz.length) {
list.get(0).setAssetNum(sz[1]);
}
}
}
}
context.put("ruleInfos", JSON.toJSONString(list.get(0)));
// 补全控件信息
Map<String, Object> map = fullInfo(list.get(0).getRuleJsonVal());
if (list.get(0).getRuleContent().contains("数量")) {
map.put("assetNum", list.get(0).getAssetNum());
}
context.put("ruleDes", JSONUtils.toJSONString(map));
}
// 初始化类型信息
Map<String, Object> RelocationTypeList = WorkOrderCst.RelocationType.getRelocationTypeList();
context.put("RelocationTypeList", RelocationTypeList);
// 物流供应商信息
context.put("logisticsSps", supplierBo.queryAllByType(WorkOrderCst.SpType.logistics.name()));
// 传入设备类型
this.setorderDeviceType(context);
}
}
上图代码是自动分配物流供应商从列表页跳转到详情页的时候,需要显示调用screen层的跟*.vm同名的*.java方法,通过传入参数id,即选择了指定行,后台会将查询到的数据封装到对象中,在vm中可以直接使用,而不用再走ajax请求,提升了系统的反映速度。其中在BaseScreen.java中负责公共日志的输出,当前权限的获取,以及一些公共属性的动态获取。但是,有一些业务场景中,我们在进行跳转的时候,只需要知道跳转过来的当前id,并不需要后端来加载数据。这时候,如果能从vm中直接获取跳转过来的url的parameter参数,那么就可以省去跟*.vm同名的*.java中的execute方法。
// 唯一正确的用法:
<input type="hidden" id="user_id" value=" $rundata.getRequest().getParameter('userId')"/> //以下几种用法都无法获取到参数的值
$!request.parameter.userId
$!request.paarmater.getParameter('userId')
此外,Velocity更多使用细节参考英文官方文档或前往此处。
三、Bug描述:$!csrfToken.hiddenField
CSRF(跨站请求伪造),它通过伪装来自受信任用户的请求来利用受信任的网站。在IDCM项目中,在*.vm页面会有大量的<from>表单提交,在表单提交的时候,为了防止跨站请求伪造,要在form标签之后紧跟$!csrfToken.ajaxUniqueToken。
<form name="xxx" action="xxx" method="post">
$!csrfToken.hiddenField
</form>
上述代码中,在VM文件的form表单中添加了token。该表单请求极有可能涉及数据增删改,需要防范CSRF,请确认使用POST请求,增加token参数,并在服务端校验token。
三、Bug描述:MyBatis中mapper层的association标签使用
API接口调用,web层/openapi/rack代码如下,RpcResult做为接口查询返回的对象,包括Data、info、Success,所以该层合理的代码应该包含try catch,正确的逻辑打印正确的信息。一旦接口调用错误,要返回合适的信息。
//根据房间和机柜名称,批量查询机柜信息 @ResourceMapping("batchQueryRackByRoomNameAndRackName")
public RpcResult<Object> batchQueryRackByRoomNameAndRackName(@RequestParam(name = "queryParam")
String queryParam) {
RpcResult<Object> rpcResult = new RpcResult<Object>();
try {
@SuppressWarnings("unchecked")
List<RackAndRoom> list = (List<RackAndRoom>) JsonToBeanUtil.JsonToJavaBean(queryParam, RackAndRoom.class);
List<Rack> rackList = new ArrayList<Rack>();
if (CollectionUtils.isNotEmpty(list)) {
for (RackAndRoom rackAndRoom : list) {
checkParameter(rackAndRoom);
Rack rack = rackBo.batchQueryRackByRoomNameAndRackName(rackAndRoom.getRackName(), rackAndRoom.getRoomName());
rackList.add(rack);
}
rpcResult.setData(rackList);
rpcResult.setInfo("查询成功!");
rpcResult.setSuccess(true);
}
} catch (Exception e) {
logger.error(" batchQueryRackByRoomNameAndRackName error:" + e.getMessage(), e);
rpcResult.setSuccess(false);
rpcResult.setInfo("查询失败,具体异常信息为:" + e.getMessage());
}
return rpcResult;
}
上述的代码结构在接口查询中,利用了分类的思想,针对接口查询成功和失败,分别对info、success、data进行赋值。并且如果失败,会有日志记录。异常的捕获在RPC层(addError)、Bo层(事务回滚)、OpenAPI层要区别对待。对于RPC中的错误,因为它是通过浏览器跟用户交互的,所以一般会将错误添加到Error对象中,将错误信息反馈给浏览器端的用户,使得用户可以修改自己的操作,达到预期的效果。
/**
* 修改排序
*/
@ResourceMapping("updateOrderIng")
public RpcResult<Object> updateOrderIng(@RequestParams
AssignRulesVo assignRulesVo, @RequestParam(name = "type")
String type, ErrorContext error) {
RpcResult<Object> result = new RpcResult<Object>();
result.setSuccess(true);
try {
if (null == assignRulesVo || null == assignRulesVo.getOrdering() || StringUtils.isBlank(type)) {
throw new SerialException("缺少排序信息和规则信息,无法修改");
}
autoAssignSupplierBo.updateOrderIng(assignRulesVo, type);
result.setInfo("修改排序成功");
result.setData(true);
} catch (Exception e) {
result.setSuccess(false);
logger.error("updateOrderIng err : ", e.getMessage(), e);
addError(error, ErrorCode.Sys_Error.getCode(), e.getMessage(), result);
}
return result;
}
对比可以看到RPC中,通常会传入Error对象,使用addError方法,将错误信息反馈到前端。接着讲openapi中的查询接口,它通过调用bo层、boImpl层、Ext层的rackMapperExt.batchQueryRackByRoomNameAndRackName(map);来查询。
<select id="batchQueryRackByRoomNameAndRackName" parameterType="java.util.Map" resultMap="BaseResultMap_Ext">
select
<include refid="Base_Column_List_Ext" />
<include refid="Base_Column_List_Room" />
<include refid="Base_Column_List_Site" />
FROM
idc_rack temp_rack
INNER JOIN idc_room temp_room ON temp_rack.room_id = temp_room.room_id
INNER JOIN idc_site temp_site ON temp_room.site_id = temp_site.site_id
WHERE
temp_rack.is_deleted = 'n'
AND temp_room.is_deleted = 'n'
AND temp_site.is_deleted = 'n'
AND temp_rack.rack_name = #{rackName}
AND temp_room.room_name = #{roomName}
</select>
在代码中,查询到的参数采用了<include>标签,将要查询的room、rack、site等信息捞出来,最后映射到BaseResultMap_Ext中,如下:
<resultMap type="com.alibaba.tboss.dal.mysql.rack.Rack" id="BaseResultMap_Ext" extends="BaseResultMap">
<result column="rack_name" property="rackName" jdbcType="VARCHAR" />
<result column="room_name" property="roomName" jdbcType="VARCHAR" />
<result column="site_name" property="siteName" jdbcType="VARCHAR" />
<result column="siteType" property="siteType" jdbcType="VARCHAR" />
<result column="lock_num" property="lockNum" jdbcType="INTEGER" />
<result column="power_state" property="powerState" jdbcType="VARCHAR" />
<association property="room" javaType="com.alibaba.tboss.dal.mysql.room.Room">
<id column="room_id" property="roomId" jdbcType="INTEGER" />
<result column="site_id" property="siteId" jdbcType="INTEGER" />
<result column="creator" property="creator" jdbcType="INTEGER" />
<result column="gmt_create" property="gmtCreate" jdbcType="TIMESTAMP" />
<result column="modifier" property="modifier" jdbcType="INTEGER" />
<result column="gmt_modified" property="gmtModified" jdbcType="TIMESTAMP" />
<result column="is_deleted" property="isDeleted" jdbcType="CHAR" />
<result column="room_name" property="roomName" jdbcType="VARCHAR" />
<result column="room_full_name" jdbcType="VARCHAR" property="roomFullName" />
<result column="type" property="type" jdbcType="VARCHAR" />
<result column="cmdb_room_id" property="cmdbRoomId" jdbcType="INTEGER" />
</association>
<association property="site" javaType="com.alibaba.tboss.dal.mysql.site.Site">
<id column="site_id" jdbcType="INTEGER" property="siteId" />
<result column="site_name" jdbcType="VARCHAR" property="siteName" />
<result column="country" jdbcType="VARCHAR" property="country" />
<result column="region" jdbcType="VARCHAR" property="region" />
<result column="province" jdbcType="VARCHAR" property="province" />
<result column="city" jdbcType="VARCHAR" property="city" />
<result column="address" jdbcType="VARCHAR" property="address" />
<result column="type" jdbcType="VARCHAR" property="type" />
<result column="site_full_name" jdbcType="VARCHAR" property="siteFullName" />
<result column="supplier" jdbcType="VARCHAR" property="supplier" />
<result column="repair_api" jdbcType="VARCHAR" property="repairApi" />
<result column="is_valid" jdbcType="BIT" property="isValid" />
<result column="creator" jdbcType="VARCHAR" property="creator" />
<result column="modifier" jdbcType="VARCHAR" property="modifier" />
<result column="gmt_create" jdbcType="TIMESTAMP" property="gmtCreate" />
<result column="gmt_modified" jdbcType="TIMESTAMP" property="gmtModified" />
<result column="is_deleted" jdbcType="CHAR" property="isDeleted" />
<result column="sign_company" jdbcType="VARCHAR" property="signCompany" />
<result column="first_user_id" jdbcType="BIGINT" property="firstUserId" />
<result column="second_user_id" jdbcType="BIGINT" property="secondUserId" />
<result column="third_user_id" jdbcType="BIGINT" property="thirdUserId" />
<result column="comments" jdbcType="VARCHAR" property="comments" />
<result column="cmdb_site_id" property="cmdbSiteId" jdbcType="INTEGER" />
</association>
</resultMap>
通过标签<association>实现了对象映射的时候的一对多的关联,上述resultMap使用extends="BaseResultMap"扩展了对象映射,其中BaseResultMap来自于Mybatis自动生成的mapper中的映射关系。
四、批量查询接口性能优化
ext层:
@Resource
public interface RackMapperExt extends RackMapper {
List<Rack> batchQueryRackByRoomNameAndRackName(List<RackVo> list);
}
mapper层:
<select id="batchQueryRackByRoomNameAndRackName" parameterType="java.util.List" resultMap="BaseResultMap_Ext">
select
<include refid="Base_Column_List_Ext" />
<include refid="Base_Column_List_Room" />
<include refid="Base_Column_List_Site" />
FROM
idc_rack temp_rack
INNER JOIN idc_room temp_room ON temp_rack.room_id = temp_room.room_id
INNER JOIN idc_site temp_site ON temp_room.site_id = temp_site.site_id
WHERE
temp_rack.is_deleted = 'n'
AND temp_room.is_deleted = 'n'
AND temp_site.is_deleted = 'n'
AND
<foreach item="item" index="index" collection="list" open="(" separator="OR" close=")">
(temp_rack.rack_name = #{item.rackName} AND temp_room.room_name = #{item.roomName})
</foreach>
</select>
五、MyBatis中<![CDATA[...]]>的使用
在xml文件中,处于CDATA部分中的所有内容都会被解析器忽略,避免由于>&等sql字符影响xml文档结构,segmentfault问题:前往此处。
六、待补充:阿里巴巴缓存使用bacardi、tail等。
附录:
阿里巴巴-基础架构事业群业务,项目路径:
基础架构事业群业务。
记录一次bug解决过程:velocity中获取url中的参数的更多相关文章
- js中获取URL中指定的查询字符串
js中获取URL中指定的搜索字符串,主要利用location对象实现,废话少说,上代码. function getSearchString(key) { // 获取URL中?之后的字符 var str ...
- VUE中获取url中的值
如图:获取值 一:main.js中写入 const router = new VueRouter({ routes: [ { path: '/goodsinfo/:goodsId', componen ...
- 如何在一次请求中通过JS中获取Url中的参数
从A跳转到B,携带参数 例如: /pc/B.jsp?item=123456 B页面在js可以直接用 var item='${param.item}'; 这样就拿到啦 还有一种方法 定义一个函数 f ...
- 记录一次bug解决过程:规范变量名称和mybatis的使用以及代码优化
一.总结 Mybatis中当parameterType为基本数据类型的时候,统一采用_parameter来代替基本数据类型变量. Mybatis中resultMap返回一个对象,resultType返 ...
- 记录一次bug解决过程:mybatis中$和#的使用
一.总结 mybatis中使用sqlMap进行sql查询时,经常需要动态传递参数.动态SQL是mybatis的强大特性之一,也是它优于其他ORM框架的一个重要原因.mybatis在对sql语句进行预编 ...
- 记录一次bug解决过程:else未补全导致数据泄露和代码优化
一.总结 快捷键ctrl + alt + 四个方向键 --> 倒置屏幕 未补全else逻辑,倒置查询数据泄露 空指针是最容易犯的错误,数据的空指针,可以普遍采用三目运算符来解决 SVN冲突解决关 ...
- 记录一次bug解决过程:数据迁移
一 总结 不擅长语言表达,勤于沟通,多锻炼 调试MyBatis中SQL语法:foreach 问题:缺少关键字VALUES.很遗憾:它的错误报的让人找不着北. 二 BUG描述:MyBatis中批量插入数 ...
- 记录一次bug解决过程:git深入学习和JDK8新特性
一 总结 熟悉廖雪峰git基础; 由于git跟踪的是修改,而不是版本号:因此对于修改撤销的操作,文件在eclipse中依旧有>修改标记,这点不同于svn. 二 BUG描述:熟悉Git基础 在Gi ...
- 记录一次bug解决过程:eclipse集成lombok插件
一 总结 eclipse集成插件lombok: 启动Spring Boot项目: sublime全局搜索关键字:ctrl + shift + F JDK8中的lambda表达式使用 二 BUG描述:集 ...
随机推荐
- [转载]强制不使用“兼容性视图”的HTML代码
在IE8浏览器以后版本,都有一个"兼容性视图",让不少新技术无法使用.那么如何禁止浏览器自动选择"兼容性视图",强制IE以最高级别的可用模式显示内容呢?下面就介 ...
- 文件随机读写专用类——RandomAccessFile
RandomAccessFile类可以随机读取文件,但是在测试中并不好用;File类可以测试文件存不存在,不存在可以创建文件;FileWriter类可以对文件进行重写或者追加内容;FileReade ...
- es6小白学习笔记(一)
1.let和const命令 1.es6新增了let和const命令,与var用法类似,但它声明的变量只在let所在的代码块内有效(块级作用域,es5只有全局和函数作用域) { let a = 1; v ...
- 基于select的python聊天室程序
python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...
- wireshark 相关提示
Packet size limited during capture 提示说明标记的包没有抓全,在某些操作系统中,默认只抓96个字节,tcpdump中有"-s"参数可用于 ...
- linux基础命令
系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...
- MMORPG大型游戏设计与开发(攻击区域 扇形)
距离上次发布已经有了很长一段时间,期间由于各种原因没有更新这方面的技术分享,在这里深表遗憾.在MMO或其他的游戏中,会有针对各种形状的计算,通常在攻击区域里不会很复杂,常见的为矩形.圆形.扇形.今天分 ...
- C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解 ...
- Android(3)—Mono For Android App版本自动更新(2)
0.前言 这篇博文是上一篇的延续,主要是修改上一个版中的BUG和优化一些待完善的项,也算是结贴,当然还有需要完善的,等日后项目中用到的时候再单独写出来吧,本篇主要写升级改进的部分: 改进1.修复[BU ...
- Entity Framework 6 Recipes 2nd Edition(13-7)译 -> 返回只部分填充的实体
问题 你有一个实体里的某个属性很少被读取或和更新,这个属性因为比较大,所以读取和更新都需要付很大的代价.你想有选择的放置这个属性 解决方案 假设你有一个如Figure 13-9 所示的模型 Figur ...