|
@@ -1,13 +1,8 @@
|
|
|
package org.ssssssss.magicapi.controller;
|
|
|
|
|
|
-import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
-import org.apache.commons.io.IOUtils;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
-import org.slf4j.event.Level;
|
|
|
-import org.springframework.core.io.InputStreamSource;
|
|
|
-import org.springframework.http.HttpHeaders;
|
|
|
import org.springframework.http.MediaType;
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.http.converter.HttpMessageConverter;
|
|
@@ -16,16 +11,12 @@ import org.springframework.http.server.ServletServerHttpRequest;
|
|
|
import org.springframework.web.bind.annotation.PathVariable;
|
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
|
|
import org.springframework.web.bind.annotation.ResponseBody;
|
|
|
-import org.springframework.web.context.request.RequestContextHolder;
|
|
|
-import org.ssssssss.magicapi.config.MagicConfiguration;
|
|
|
-import org.ssssssss.magicapi.config.MappingHandlerMapping;
|
|
|
-import org.ssssssss.magicapi.config.Valid;
|
|
|
+import org.ssssssss.magicapi.config.*;
|
|
|
import org.ssssssss.magicapi.context.CookieContext;
|
|
|
import org.ssssssss.magicapi.context.RequestContext;
|
|
|
import org.ssssssss.magicapi.context.SessionContext;
|
|
|
import org.ssssssss.magicapi.exception.ValidateException;
|
|
|
import org.ssssssss.magicapi.interceptor.RequestInterceptor;
|
|
|
-import org.ssssssss.magicapi.logging.LogInfo;
|
|
|
import org.ssssssss.magicapi.logging.MagicLoggerContext;
|
|
|
import org.ssssssss.magicapi.model.*;
|
|
|
import org.ssssssss.magicapi.modules.ResponseModule;
|
|
@@ -44,13 +35,13 @@ import org.ssssssss.script.reflection.JavaInvoker;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import java.io.IOException;
|
|
|
-import java.io.InputStream;
|
|
|
import java.lang.reflect.Method;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import static org.springframework.http.HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS;
|
|
|
+import static org.ssssssss.magicapi.config.MessageType.BREAKPOINT;
|
|
|
import static org.ssssssss.magicapi.model.Constants.*;
|
|
|
|
|
|
public class RequestHandler extends MagicController {
|
|
@@ -74,14 +65,9 @@ public class RequestHandler extends MagicController {
|
|
|
public Object invoke(HttpServletRequest request, HttpServletResponse response,
|
|
|
@PathVariable(required = false) Map<String, Object> pathVariables,
|
|
|
@RequestParam(required = false) Map<String, Object> parameters) throws Throwable {
|
|
|
- RequestEntity requestEntity = new RequestEntity(request, response, isRequestedFromTest(request), parameters, pathVariables);
|
|
|
- if (requestEntity.isRequestedFromTest()) {
|
|
|
- response.setHeader(HEADER_RESPONSE_WITH_MAGIC_API, CONST_STRING_TRUE);
|
|
|
- response.setHeader(ACCESS_CONTROL_EXPOSE_HEADERS, HEADER_RESPONSE_WITH_MAGIC_API);
|
|
|
- if(requestEntity.isRequestedFromContinue()){
|
|
|
- return invokeContinueRequest(requestEntity);
|
|
|
- }
|
|
|
- }
|
|
|
+ String sessionId = null;
|
|
|
+ boolean requestedFromTest = configuration.isEnableWeb() && (sessionId = request.getHeader(HEADER_REQUEST_SESSION)) != null;
|
|
|
+ RequestEntity requestEntity = new RequestEntity(request, response, requestedFromTest, parameters, pathVariables);
|
|
|
if (requestEntity.getApiInfo() == null) {
|
|
|
logger.error("{}找不到对应接口", request.getRequestURI());
|
|
|
return buildResult(requestEntity, API_NOT_FOUND, "接口不存在");
|
|
@@ -115,8 +101,7 @@ public class RequestHandler extends MagicController {
|
|
|
}}, BODY_INVALID);
|
|
|
}
|
|
|
} catch (ValidateException e) {
|
|
|
- Object value = resultProvider.buildResult(requestEntity, RESPONSE_CODE_INVALID, e.getMessage());
|
|
|
- return requestEntity.isRequestedFromTest() ? new JsonBean<>(e.getJsonCode(), value) : value;
|
|
|
+ return resultProvider.buildResult(requestEntity, RESPONSE_CODE_INVALID, e.getMessage());
|
|
|
} catch (Throwable root) {
|
|
|
return processException(requestEntity, root);
|
|
|
}
|
|
@@ -126,13 +111,18 @@ public class RequestHandler extends MagicController {
|
|
|
Object value;
|
|
|
// 执行前置拦截器
|
|
|
if ((value = doPreHandle(requestEntity)) != null) {
|
|
|
- if (requestEntity.isRequestedFromTest()) {
|
|
|
- // 修正前端显示,当拦截器返回时,原样输出显示
|
|
|
- response.setHeader(HEADER_RESPONSE_WITH_MAGIC_API, CONST_STRING_FALSE);
|
|
|
- }
|
|
|
return value;
|
|
|
}
|
|
|
- return requestEntity.isRequestedFromTest() ? invokeTestRequest(requestEntity) : invokeRequest(requestEntity);
|
|
|
+ if(requestedFromTest){
|
|
|
+ try {
|
|
|
+ MagicLoggerContext.SESSION.set(sessionId);
|
|
|
+ return invokeRequest(requestEntity);
|
|
|
+ } finally {
|
|
|
+ MagicLoggerContext.SESSION.remove();
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ return invokeRequest(requestEntity);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private Object buildResult(RequestEntity requestEntity, JsonCode code, Object data) {
|
|
@@ -249,61 +239,6 @@ public class RequestHandler extends MagicController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private Object invokeContinueRequest(RequestEntity requestEntity) throws Exception {
|
|
|
- HttpServletRequest request = requestEntity.getRequest();
|
|
|
- MagicScriptDebugContext context = MagicScriptDebugContext.getDebugContext(requestEntity.getRequestedSessionId());
|
|
|
- if (context == null) {
|
|
|
- return new JsonBean<>(DEBUG_SESSION_NOT_FOUND, buildResult(requestEntity, DEBUG_SESSION_NOT_FOUND, null));
|
|
|
- }
|
|
|
- // 重置断点
|
|
|
- context.setBreakpoints(requestEntity.getRequestedBreakpoints());
|
|
|
- // 步进
|
|
|
- context.setStepInto(CONST_STRING_TRUE.equalsIgnoreCase(request.getHeader(HEADER_REQUEST_STEP_INTO)));
|
|
|
- try {
|
|
|
- context.singal(); //等待语句执行到断点或执行完毕
|
|
|
- } catch (InterruptedException e) {
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
- if (context.isRunning()) { //判断是否执行完毕
|
|
|
- return new JsonBodyBean<>(1000, context.getId(), resultProvider.buildResult(requestEntity, 1000, context.getId()), context.getDebugInfo());
|
|
|
- } else if (context.isException()) {
|
|
|
- return resolveThrowableForTest(requestEntity, (Throwable) context.getReturnValue());
|
|
|
- }
|
|
|
- Object value = context.getReturnValue();
|
|
|
- // 执行后置拦截器
|
|
|
- if ((value = doPostHandle(requestEntity, value)) != null) {
|
|
|
- // 修正前端显示,当拦截器返回时,原样输出显示
|
|
|
- requestEntity.getResponse().setHeader(HEADER_RESPONSE_WITH_MAGIC_API, CONST_STRING_FALSE);
|
|
|
- // 后置拦截器不包裹
|
|
|
- return value;
|
|
|
- }
|
|
|
- return convertResult(requestEntity, context.getReturnValue());
|
|
|
- }
|
|
|
-
|
|
|
- private Object invokeTestRequest(RequestEntity requestEntity) {
|
|
|
- try {
|
|
|
- // 初始化debug操作
|
|
|
- MagicScriptDebugContext context = initializeDebug(requestEntity);
|
|
|
- Object result = ScriptManager.executeScript(requestEntity.getApiInfo().getScript(), requestEntity.getMagicScriptContext());
|
|
|
- if (context.isRunning()) {
|
|
|
- return new JsonBodyBean<>(1000, context.getId(), resultProvider.buildResult(requestEntity, 1000, context.getId(), result), result);
|
|
|
- } else if (context.isException()) { //判断是否出现异常
|
|
|
- return resolveThrowableForTest(requestEntity, (Throwable) context.getReturnValue());
|
|
|
- }
|
|
|
- Object value = result;
|
|
|
- // 执行后置拦截器
|
|
|
- if ((value = doPostHandle(requestEntity, value)) != null) {
|
|
|
- // 修正前端显示,当拦截器返回时,原样输出显示
|
|
|
- requestEntity.getResponse().setHeader(HEADER_RESPONSE_WITH_MAGIC_API, CONST_STRING_FALSE);
|
|
|
- // 后置拦截器不包裹
|
|
|
- return value;
|
|
|
- }
|
|
|
- return convertResult(requestEntity, result);
|
|
|
- } catch (Exception e) {
|
|
|
- return resolveThrowableForTest(requestEntity, e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private Object invokeRequest(RequestEntity requestEntity) throws Throwable {
|
|
|
try {
|
|
|
Object result = ScriptManager.executeScript(requestEntity.getApiInfo().getScript(), requestEntity.getMagicScriptContext());
|
|
@@ -322,125 +257,28 @@ public class RequestHandler extends MagicController {
|
|
|
}
|
|
|
|
|
|
private Object processException(RequestEntity requestEntity, Throwable root) throws Throwable {
|
|
|
+ MagicScriptException se = null;
|
|
|
Throwable parent = root;
|
|
|
do {
|
|
|
if (parent instanceof MagicScriptAssertException) {
|
|
|
MagicScriptAssertException sae = (MagicScriptAssertException) parent;
|
|
|
return resultProvider.buildResult(requestEntity, sae.getCode(), sae.getMessage());
|
|
|
}
|
|
|
+ if (parent instanceof MagicScriptException) {
|
|
|
+ se = (MagicScriptException) parent;
|
|
|
+ }
|
|
|
} while ((parent = parent.getCause()) != null);
|
|
|
if (configuration.isThrowException()) {
|
|
|
throw root;
|
|
|
}
|
|
|
logger.error("接口{}请求出错", requestEntity.getRequest().getRequestURI(), root);
|
|
|
- return resultProvider.buildException(requestEntity, root);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 转换请求结果
|
|
|
- */
|
|
|
- private Object convertResult(RequestEntity requestEntity, Object result) throws IOException {
|
|
|
- if (result instanceof ResponseEntity) {
|
|
|
- ResponseEntity<?> entity = (ResponseEntity<?>) result;
|
|
|
- List<String> headers = new ArrayList<>();
|
|
|
- for (Map.Entry<String, List<String>> entry : entity.getHeaders().entrySet()) {
|
|
|
- String key = entry.getKey();
|
|
|
- for (String value : entry.getValue()) {
|
|
|
- headers.add(HEADER_PREFIX_FOR_TEST + key);
|
|
|
- requestEntity.getResponse().addHeader(HEADER_PREFIX_FOR_TEST + key, value);
|
|
|
- }
|
|
|
- }
|
|
|
- headers.add(HEADER_RESPONSE_WITH_MAGIC_API);
|
|
|
- // 允许前端读取自定义的header(跨域情况)。
|
|
|
- requestEntity.getResponse().setHeader(ACCESS_CONTROL_EXPOSE_HEADERS, String.join(",", headers));
|
|
|
- if (entity.getHeaders().isEmpty()) {
|
|
|
- return ResponseEntity.ok(new JsonBean<>(entity.getBody()));
|
|
|
- }
|
|
|
- return ResponseEntity.ok(new JsonBean<>(convertToBase64(entity.getBody())));
|
|
|
- } else if (result instanceof ResponseModule.NullValue) {
|
|
|
- // 对于return response.end() 的特殊处理
|
|
|
- return new JsonBean<>(RESPONSE_CODE_SUCCESS, "empty.");
|
|
|
- }
|
|
|
- return new JsonBean<>(resultProvider.buildResult(requestEntity, result));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 将结果转为base64
|
|
|
- */
|
|
|
- private String convertToBase64(Object value) throws IOException {
|
|
|
- if (value instanceof String || value instanceof Number) {
|
|
|
- return convertToBase64(value.toString().getBytes());
|
|
|
- } else if (value instanceof byte[]) {
|
|
|
- return Base64.getEncoder().encodeToString((byte[]) value);
|
|
|
- } else if (value instanceof InputStream) {
|
|
|
- return convertToBase64(IOUtils.toByteArray((InputStream) value));
|
|
|
- } else if (value instanceof InputStreamSource) {
|
|
|
- InputStreamSource iss = (InputStreamSource) value;
|
|
|
- return convertToBase64(iss.getInputStream());
|
|
|
- } else {
|
|
|
- return convertToBase64(new ObjectMapper().writeValueAsString(value));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 解决异常
|
|
|
- */
|
|
|
- private JsonBean<Object> resolveThrowableForTest(RequestEntity requestEntity, Throwable root) {
|
|
|
- MagicScriptException se = null;
|
|
|
- Throwable parent = root;
|
|
|
- do {
|
|
|
- if (parent instanceof MagicScriptAssertException) {
|
|
|
- MagicScriptAssertException sae = (MagicScriptAssertException) parent;
|
|
|
- return new JsonBean<>(resultProvider.buildResult(requestEntity, sae.getCode(), sae.getMessage()));
|
|
|
- }
|
|
|
- if (parent instanceof MagicScriptException) {
|
|
|
- se = (MagicScriptException) parent;
|
|
|
- }
|
|
|
- } while ((parent = parent.getCause()) != null);
|
|
|
- logger.error("测试脚本出错", root);
|
|
|
- if (se != null) {
|
|
|
+ if(se != null && requestEntity.isRequestedFromTest()){
|
|
|
Span.Line line = se.getLine();
|
|
|
+ requestEntity.getResponse().setHeader(ACCESS_CONTROL_EXPOSE_HEADERS, HEADER_RESPONSE_WITH_MAGIC_API);
|
|
|
+ requestEntity.getResponse().setHeader(HEADER_RESPONSE_WITH_MAGIC_API, CONST_STRING_TRUE);
|
|
|
return new JsonBodyBean<>(-1000, se.getSimpleMessage(), resultProvider.buildException(requestEntity, se), line == null ? null : Arrays.asList(line.getLineNumber(), line.getEndLineNumber(), line.getStartCol(), line.getEndCol()));
|
|
|
}
|
|
|
- return new JsonBean<>(-1, root.getMessage(), resultProvider.buildException(requestEntity, root));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 初始化DEBUG
|
|
|
- */
|
|
|
- private MagicScriptDebugContext initializeDebug(RequestEntity requestEntity) {
|
|
|
- MagicScriptDebugContext context = (MagicScriptDebugContext) requestEntity.getMagicScriptContext();
|
|
|
- HttpServletRequest request = requestEntity.getRequest();
|
|
|
- // 由于debug是开启一个新线程,为了防止在子线程中无法获取request对象,所以将request放在InheritableThreadLocal中。
|
|
|
- RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
|
|
|
-
|
|
|
- String sessionId = requestEntity.getRequestedSessionId();
|
|
|
- // 设置断点
|
|
|
- context.setBreakpoints(requestEntity.getRequestedBreakpoints());
|
|
|
- context.setTimeout(configuration.getDebugTimeout());
|
|
|
- context.setId(sessionId);
|
|
|
- // 设置相关回调,打印日志,回收资源
|
|
|
- context.onComplete(() -> {
|
|
|
- if (context.isException()) {
|
|
|
- MagicLoggerContext.println(new LogInfo(Level.ERROR.name().toLowerCase(), "执行脚本出错", (Throwable) context.getReturnValue()));
|
|
|
- }
|
|
|
- logger.info("Close Console Session : {}", sessionId);
|
|
|
- RequestContext.remove();
|
|
|
- MagicLoggerContext.remove(sessionId);
|
|
|
- });
|
|
|
- context.onStart(() -> {
|
|
|
- RequestContext.setRequestEntity(requestEntity);
|
|
|
- MagicLoggerContext.SESSION.set(sessionId);
|
|
|
- logger.info("Create Console Session : {}", sessionId);
|
|
|
- });
|
|
|
- return context;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判断是否是测试请求
|
|
|
- */
|
|
|
- private boolean isRequestedFromTest(HttpServletRequest request) {
|
|
|
- return configuration.isEnableWeb() && request.getHeader(HEADER_REQUEST_SESSION) != null;
|
|
|
+ return resultProvider.buildException(requestEntity, root);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -466,9 +304,22 @@ public class RequestHandler extends MagicController {
|
|
|
/**
|
|
|
* 构建 MagicScriptContext
|
|
|
*/
|
|
|
- private MagicScriptContext createMagicScriptContext(RequestEntity requestEntity, Object requestBody) throws IOException {
|
|
|
+ private MagicScriptContext createMagicScriptContext(RequestEntity requestEntity, Object requestBody) {
|
|
|
+ List<Integer> breakpoints = requestEntity.getRequestedBreakpoints();
|
|
|
// 构建脚本上下文
|
|
|
- MagicScriptContext context = requestEntity.isRequestedFromTest() ? new MagicScriptDebugContext() : new MagicScriptContext();
|
|
|
+ MagicScriptContext context;
|
|
|
+ // TODO 安全校验
|
|
|
+ if(requestEntity.isRequestedFromDebug()){
|
|
|
+ MagicScriptDebugContext debugContext = new MagicScriptDebugContext(breakpoints);
|
|
|
+ String sessionId = requestEntity.getRequestedSessionId();
|
|
|
+ debugContext.setTimeout(configuration.getDebugTimeout());
|
|
|
+ debugContext.setId(sessionId);
|
|
|
+ WebSocketSessionManager.setSessionAttribute(sessionId, WS_DEBUG_MAGIC_SCRIPT_CONTEXT, debugContext);
|
|
|
+ debugContext.setCallback(variables -> WebSocketSessionManager.sendBySessionId(sessionId, BREAKPOINT, variables));
|
|
|
+ context = debugContext;
|
|
|
+ }else{
|
|
|
+ context = new MagicScriptContext();
|
|
|
+ }
|
|
|
Object wrap = requestEntity.getApiInfo().getOptionValue(Options.WRAP_REQUEST_PARAMETERS.getValue());
|
|
|
if (wrap != null && StringUtils.isNotBlank(wrap.toString())) {
|
|
|
context.set(wrap.toString(), requestEntity.getParameters());
|
|
@@ -514,19 +365,11 @@ public class RequestHandler extends MagicController {
|
|
|
* 执行前置拦截器
|
|
|
*/
|
|
|
private Object doPreHandle(RequestEntity requestEntity) throws Exception {
|
|
|
- try {
|
|
|
- for (RequestInterceptor requestInterceptor : configuration.getRequestInterceptors()) {
|
|
|
- Object value = requestInterceptor.preHandle(requestEntity);
|
|
|
- if (value != null) {
|
|
|
- return value;
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- if (requestEntity.isRequestedFromTest()) {
|
|
|
- // 修正前端显示,原样输出显示
|
|
|
- requestEntity.getResponse().setHeader(HEADER_RESPONSE_WITH_MAGIC_API, CONST_STRING_FALSE);
|
|
|
+ for (RequestInterceptor requestInterceptor : configuration.getRequestInterceptors()) {
|
|
|
+ Object value = requestInterceptor.preHandle(requestEntity);
|
|
|
+ if (value != null) {
|
|
|
+ return value;
|
|
|
}
|
|
|
- throw e;
|
|
|
}
|
|
|
return null;
|
|
|
}
|