2021SC@SDUSC
AbstractParser类(解析器)
将request解析为SQL集
对应的相关核心代码:(在这里需要注意的是这个角色判断必须在parseCorrectRequest后面,因为parseCorrectRequest可能会添加 @role)
/**解析请求json并获取对应结果
* @param request
* @return requestObject
*/
public JSONObject parseResponse(JSONObject request) {
long startTime = System.currentTimeMillis();
Log.d(TAG, "parseResponse startTime = " + startTime
+ "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n\n ");
requestObject = request;
verifier = createVerifier().setVisitor(getVisitor());
if (RequestMethod.isPublicMethod(requestMethod) == false) {
try {
if (isNeedVerifyLogin()) {
onVerifyLogin();
}
if (isNeedVerifyContent()) {
onVerifyContent();
}
} catch (Exception e) {
return extendErrorResult(requestObject, e);
}
}
if (isNeedVerifyRole() && globleRole == null) {
try {
setGlobleRole(RequestRole.get(requestObject.getString(JSONRequest.KEY_ROLE)));
requestObject.remove(JSONRequest.KEY_ROLE);
} catch (Exception e) {
return extendErrorResult(requestObject, e);
}
}
try {
setGlobleFormat(requestObject.getBoolean(JSONRequest.KEY_FORMAT));
setGlobleDatabase(requestObject.getString(JSONRequest.KEY_DATABASE));
setGlobleSchema(requestObject.getString(JSONRequest.KEY_SCHEMA));
setGlobleDatasource(requestObject.getString(JSONRequest.KEY_DATASOURCE));
setGlobleExplain(requestObject.getBoolean(JSONRequest.KEY_EXPLAIN));
setGlobleCache(requestObject.getString(JSONRequest.KEY_CACHE));
requestObject.remove(JSONRequest.KEY_FORMAT);
requestObject.remove(JSONRequest.KEY_DATABASE);
requestObject.remove(JSONRequest.KEY_SCHEMA);
requestObject.remove(JSONRequest.KEY_DATASOURCE);
requestObject.remove(JSONRequest.KEY_EXPLAIN);
requestObject.remove(JSONRequest.KEY_CACHE);
} catch (Exception e) {
return extendErrorResult(requestObject, e);
}
final String requestString = JSON.toJSONString(request);//request传进去解析后已经变了
queryResultMap = new HashMap<String, Object>();
Exception error = null;
sqlExecutor = createSQLExecutor();
onBegin();
try {
queryDepth = 0;
requestObject = onObjectParse(request, null, null, null, false);
onCommit();
}
catch (Exception e) {
e.printStackTrace();
error = e;
onRollback();
}
requestObject = error == null ? extendSuccessResult(requestObject) : extendErrorResult(requestObject, error);
JSONObject res = (globleFormat != null && globleFormat) && JSONResponse.isSuccess(requestObject) ? new JSONResponse(requestObject) : requestObject;
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
if (Log.DEBUG) {
requestObject.put("sql:generate|cache|execute|maxExecute", getSQLExecutor().getGeneratedSQLCount() + "|" + getSQLExecutor().getCachedSQLCount() + "|" + getSQLExecutor().getExecutedSQLCount() + "|" + getMaxSQLCount());
requestObject.put("depth:count|max", queryDepth + "|" + getMaxQueryDepth());
requestObject.put("time:start|duration|end", startTime + "|" + duration + "|" + endTime);
if (error != null) {
requestObject.put("throw", error.getClass().getName());
requestObject.put("trace", error.getStackTrace());
}
}
onClose();
if (IS_PRINT_REQUEST_STRING_LOG||Log.DEBUG||error != null) {
Log.sl("\n\n\n",'<',"");
Log.fd(TAG , requestMethod + "/parseResponse request = \n" + requestString + "\n\n");
}
if (IS_PRINT_BIG_LOG||Log.DEBUG||error != null) { // 日志仅存服务器,所以不太敏感,而且这些日志虽然量大但非常重要,对排查 bug 很关键
Log.fd(TAG,requestMethod + "/parseResponse return response = \n" + JSON.toJSONString(requestObject) + "\n\n");
}
if (IS_PRINT_REQUEST_ENDTIME_LOG||Log.DEBUG||error != null) {
Log.fd(TAG , requestMethod + "/parseResponse endTime = " + endTime + "; duration = " + duration);
Log.sl("",'>',"\n\n\n");
}
return res;
}
首先就是在进行解析前对用户的身份进行一个验证,并且获取得到当前的用户身份。
在此中将JSON对象类型的request类型再次去调用了JSON的toJSONString方法,而这个最终调用了fastjson包的下列方法,将request再次解析成为更适合进行SQL处理的String类型的JSON数据,而且这次解析后request就已经变化了,下面是在拓展库fastjson中的最终调用方法,在这里主要是进行了一个格式的过滤。:
/**
* @since 1.2.9
* @return
*/
public static String toJSONString(Object object, //
SerializeConfig config, //
SerializeFilter[] filters, //
String dateFormat, //
int defaultFeatures, //
SerializerFeature... features) {
SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);
try {
JSONSerializer serializer = new JSONSerializer(out, config);
if (dateFormat != null && dateFormat.length() != 0) {
serializer.setDateFormat(dateFormat);
serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
}
if (filters != null) {
for (SerializeFilter filter : filters) {
serializer.addFilter(filter);
}
}
serializer.write(object);
return out.toString();
} finally {
out.close();
}
}
我么你可以看到,在这里面其实有一个序列化的对象,关于序列化与反序列化的概念,我们后续的分析中会再进行,所以这里先不赘述,我们在这里主要分析它的作用,也就是我们制作了一个配置,然后我们根据我们制定的配置进行序列化和反序列化,以便于达到信息的传输的功能。这个怎么理解呢?比如说我现在要送一个有着复杂结构的椅子从A城市到B城市,我要把它们拆开打包再送出去,然后我们怎么进行的拆解,回来组装的时候便怎么进行安装,这个组装规则就相当于是我们的配置规则。
身份验证及获取
在解析类中,还有比较重要的点就是身份角色的验证以及获取,以便于得到相应的角色作用
/**校验角色及对应操作的权限
* @param config
* @return
* @throws Exception
*/
@Override
public void onVerifyRole(@NotNull SQLConfig config) throws Exception {
if (Log.DEBUG) {
Log.i(TAG, "onVerifyRole config = " + JSON.toJSONString(config));
}
if (isNeedVerifyRole()) {
if (config.getRole() == null) {
if (globleRole != null) {
config.setRole(globleRole);
} else {
config.setRole(getVisitor().getId() == null ? RequestRole.UNKNOWN : RequestRole.LOGIN);
}
}
getVerifier().verifyAccess(config);
}
}
首先在这个config里面是有role角色的相关属性,同时我们的很多需要验证的方法都会去进行一个验证,当不存在时,那便进行设置,同时更新config的相关信息,否则就是直接获取。这个就是进行设置的具体,首先就是判断一下,当前是否需要用户权限,如果是游客是不需要的。如果需要那便查看config对象的role是否为null,如果为null则我们需要进行设置,那么就把当前的visitor设置为role,如果visitor也为null,按照理论来说不会这样,但如果发生了就把role设置为未知角色。
正确的JSON结构
下面是当获得前端request时的格式正误判断以及一些请求的要求格式的检查,必须获取到指定的JSON结构:
/**解析请求JSONObject
* @param request => URLDecoder.decode(request, UTF_8);
* @return
* @throws Exception
*/
@NotNull
public static JSONObject parseRequest(String request) throws Exception {
JSONObject obj = JSON.parseObject(request);
if (obj == null) {
throw new UnsupportedEncodingException("JSON格式不合法!");
}
return obj;
}
@Override
public JSONObject parseCorrectRequest(RequestMethod method, String tag, int version, String name, @NotNull JSONObject request
, int maxUpdateCount, SQLCreator creator) throws Exception {
if (RequestMethod.isPublicMethod(method)) {
return request;
}
if (StringUtil.isEmpty(tag, true)) {
throw new IllegalArgumentException("请在最外层传 tag !一般是 Table 名,例如 \"tag\": \"User\" ");
}
//代码在此处获取指定的JSON结构 <<<<<<<<<<<<
JSONObject object = null;
String error = "";
try {
object = getStructure("Request", method.name(), tag, version);
} catch (Exception e) {
error = e.getMessage();
}
if (object == null) { //empty表示随意操作 || object.isEmpty()) {
throw new UnsupportedOperationException("找不到 version: " + version + ", method: " + method.name() + ", tag: " + tag + " 对应的 structure !"
+ "非开放请求必须是后端 Request 表中校验规则允许的操作!\n " + error + "\n如果需要则在 Request 表中新增配置!");
}
JSONObject target = object;
if (object.containsKey(tag) == false) { //tag 是 Table 名或 Table[]
boolean isArrayKey = tag.endsWith(":[]"); // JSONRequest.isArrayKey(tag);
String key = isArrayKey ? tag.substring(0, tag.length() - 3) : tag;
if (apijson.JSONObject.isTableKey(key)) {
if (isArrayKey) { //自动为 tag = Comment:[] 的 { ... } 新增键值对 "Comment[]":[] 为 { "Comment[]":[], ... }
target.put(key + "[]", new JSONArray());
}
else { //自动为 tag = Comment 的 { ... } 包一层为 { "Comment": { ... } }
target = new JSONObject(true);
target.put(tag, object);
}
}
}
//JSONObject clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {}
return getVerifier().verifyRequest(method, name, target, request, maxUpdateCount, getGlobleDatabase(), getGlobleSchema(), creator);
}
在这里面的这一段代码
if (RequestMethod.isPublicMethod(method)) {
return request;
}
我们都知道POST方法的安全性一般是要高于GET方法,我们这一段就是需要指定JSON结构的GET请求可以改为POST请求。但这个一般只有对安全性要求高的才会指定,因为在这种情况下用明文的GET方式几乎肯定不安全。
这个核实的过程,首先就是通过getStructure获取到相应属性值,然后对这个JSONObject对象进行初始判断,检查是否存在相关的表名以及是否请求的是数组对象,然后通过verifier对象去进行核实且获取到返回的request。
接下来对刚才的操作进行了性能优化:
/**
* 获取正确的返回结果
*
* @param method
* @param response
* @return
* @throws Exception
*/
@Override
public JSONObject parseCorrectResponse(String table, JSONObject response) throws Exception {
Log.d(TAG, "getCorrectResponse method = " + method + "; table = " + table);
if (response == null || response.isEmpty()) {//避免无效空result:{}添加内容后变有效
Log.e(TAG, "getCorrectResponse response == null || response.isEmpty() >> return response;");
return response;
}
JSONObject target = apijson.JSONObject.isTableKey(table) == false
? new JSONObject() : getStructure(method, "Response", "model", table);
return MethodStructure.parseResponse(method, table, target, response, new OnParseCallback() {
@Override
protected JSONObject onParseJSONObject(String key, JSONObject tobj, JSONObject robj) throws Exception {
return getCorrectResponse(method, key, robj);
}
});
}
这个其实相当于一个动态规划,也就是我们所说的查表法,当检测到reaponse为null或者为空时,那么便直接return,避免了接下来的一些操作,同时也进行了一个避免,就是我们的原本无效的空result可能在进行了后续的操作便会变得有效,但这是不期望发生的,所以在这里进行一个判断就避免掉了这个情况。
与小组其他成员讨论这次主要是分析parseCorrectRequest方法的时候,在最后它是进行了一个返回:
return getVerifier().verifyRequest(method, name, target, request, maxUpdateCount, getGlobleDatabase(), getGlobleSchema(), creator);
而这个是属于队友的分析范围,秉持着可以多不可以缺的原则,我先是自己查看了相关的代码,由于代码量相当庞大,短时间觉得不甚理解,便与负责这一部分代码的队友进行了交流,然后便大致理解了这一行代码的作用,也对上下文更加的了解,这一行便是去先进行角色的核验,然后核验该角色的对应权限,然后根据request与response的不同情况,来解析得到不同的返回结果。