package ${basePackage}.action; import com.fasterxml.jackson.core.TreeNode; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.View; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; import ${basePackage}.config.ActionConfig; import ${basePackage}.frame.auth.LocalData; import ${basePackage}.frame.base.BaseRequest; import ${basePackage}.frame.base.BaseResponse; import ${basePackage}.frame.base.ErrorType; import ${basePackage}.frame.base.Screen; import ${basePackage}.frame.auth.Token; import ${basePackage}.frame.utils.AESUtil; import ${basePackage}.frame.utils.LogUtil; import ${basePackage}.frame.utils.MD5Util; import ${basePackage}.frame.utils.MapperUtil; import ${basePackage}.frame.utils.Properties; import ${basePackage}.frame.utils.RequestUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 全局请求Controller,如果无特殊请求,则不需再增加其他Controller * 全局htm后缀入口{@link GlobalController#action(Model, HttpServletRequest, HttpServletResponse)} * 全局ajax入口{@link GlobalController#ajax(String, String, String, HttpServletRequest, HttpServletResponse, String, MultipartFile)} * 全局异常捕捉{@link GlobalController#exceptionHandler(HttpServletRequest, HttpServletResponse, Model, Exception)} * 全局消息订阅{@link GlobalController#sse(String)} *
* 说明Request命名规则,驼峰式命名
* Api#Example#Request ==> 目标#动作#Request
*
* @author author
* @version 0.0.1
* @since 2017-01-01
*/
@Controller
@ControllerAdvice
public class GlobalController implements ErrorController {
@Autowired
private FreeMarkerViewResolver viewResolver;
/**
* 全局异常捕捉
*
* @param request
* @param response
* @param exception 要捕获的异常
* @return
*/
@ExceptionHandler(Exception.class)
public String exceptionHandler(HttpServletRequest request, HttpServletResponse response, Model model, Exception exception) {
StringBuffer msg = new StringBuffer("");
if (exception != null) {
msg = new StringBuffer("");
String message = exception.toString();
int length = exception.getStackTrace().length;
if (length > 0) {
msg.append("").append(message).append("
");
for (int i = 0; i < length; i++) {
msg.append("").append(exception.getStackTrace()[i]).append("
");
}
} else {
msg.append(message);
}
}
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
model.addAttribute("msg", msg.toString());
return "500";
}
private final static String ERROR_PATH = "/error";
@Override
public String getErrorPath() {
return ERROR_PATH;
}
@RequestMapping(value = ERROR_PATH)
public String error(HttpServletRequest request, HttpServletResponse response) {
switch (response.getStatus()) {
case 404:
return "404";
case 403:
String errorUrl = RequestUtil.getErrorUrl(request);
if ((errorUrl.equals(Properties.homePage) || errorUrl.equals("/")) && LocalData.getToken() == null) {
try {
response.sendRedirect("/login.htm");
} catch (IOException e) {
e.printStackTrace();
}
}
return "403";
case 500:
return "500";
default:
return "403";
}
}
@RequestMapping("/")
public String home() {
Token token = LocalData.getToken();
if (token == null) {
return "redirect:" + Properties.context + Properties.loginPage;
} else {
return "redirect:" + Properties.context + Properties.homePage;
}
}
/**
* 当未明确指定控制器时,走该请求,默认返回对应的layout布局和screen视图
* 当需要使用layout时,不需要返回值,ViewNameTranslator会处理对应关系
*
* @param model
* @param request
*/
@RequestMapping({"/**/*.htm"})
public String action(Model model, HttpServletRequest request, HttpServletResponse response) {
String servletPath = request.getServletPath();// /**/*.htm
String layout = "/layout/default";
String action = LocalData.getAction();// **/*
Pattern compile = Pattern.compile("^/(.+)\\.htm");
Matcher matcher = compile.matcher(servletPath);
if (matcher.find()) {
action = matcher.group(1);
LocalData.setAction(action);
}
try {
LocaleResolver localeResolver = (LocaleResolver) request.getAttribute(DispatcherServlet.LOCALE_RESOLVER_ATTRIBUTE);
Locale locale = localeResolver.resolveLocale(request);
{//查询screen
String[] split = action.split("/");
StringBuilder sb = new StringBuilder("");
sb.append("screen");
for (int i = 0; i < split.length; i++) {
sb.append(File.separator);
sb.append(split[i]);
}
layout = sb.toString();
View view = viewResolver.resolveViewName(layout, locale);
if (view == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,"");
return null;
}
// 尝试执行Screen执行器(服务器渲染),并返回视图模板
try {
String beanClassName = (ActionConfig.SCREEN_PREFIX + action).toLowerCase();
Screen screenExec = LocalData.getApplicationContext().getBean(beanClassName, Screen.class);
screenExec.exec(model, request, response);
if (response.getStatus() != HttpServletResponse.SC_OK) {
response.sendError(response.getStatus(),"");
return null;
}
} catch (BeansException e) {
}
}
{//查找layout
String[] split = action.split("/");
int lt = split.length;
while (lt > 0) {
StringBuilder sb = new StringBuilder("");
sb.append("layout");
for (int i = 0; i < lt - 1; i++) {
sb.append(File.separator);
sb.append(split[i]);
}
layout = sb.toString() + File.separator + split[split.length - 1];
View view = viewResolver.resolveViewName(layout, locale);
//无法找到对应layout,使用默认layout
if (view == null) {
layout = sb.toString() + File.separator + "default";
View defaultView = viewResolver.resolveViewName(layout, locale);
if (null == defaultView && lt == 1) {
System.err.println("can not find layout/default.ftl");
} else if (null == defaultView) {
lt--;
} else {
break;
}
} else {
break;
}
}
}
} catch (Exception e) {
return exceptionHandler(request, response, model, e);
}
//todo 可在此获取共性数据(也可以在全局拦截器GlobalHandlerInterceptor、拦截器作用域比此更高),
//todo 例如用户信息等。其他业务数据在页面渲染后通过Ajax请求
return layout;
}
@RequestMapping("/ajax/{module}/{target}/{method}")
@ResponseBody
public Object ajax(
@PathVariable String module,
@PathVariable String target,
@PathVariable String method,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
@RequestBody(required = false) String data,
@RequestParam(name = "file", required = false) MultipartFile file) {
try {
String beanClassName = (ActionConfig.AJAX_PREFIX + module + "/" + target).toLowerCase();
Object ajax = LocalData.getApplicationContext().getBean(beanClassName);
Class ajaxClass = ajax.getClass();
Method[] methods = ajaxClass.getDeclaredMethods();
Method methodC = null;
for (Method meth : methods) {
if (meth.getName().equals(method)) {
methodC = meth;
}
}
if (methodC == null) {
BaseResponse baseResponse = new BaseResponse();
baseResponse.addError(ErrorType.BUSINESS_ERROR, "未找到对应的方法!");
return baseResponse;
}
Parameter[] parameters = methodC.getParameters();
Object[] arg = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
if (parameter.getType() == HttpServletRequest.class) {
arg[i] = httpServletRequest;
} else if (parameter.getType() == HttpServletResponse.class) {
arg[i] = httpServletResponse;
} else if (parameter.getType() == TreeNode.class) {
arg[i] = MapperUtil.toTree(data);
} else if (parameter.getType() == String.class) {
arg[i] = data;
} else if (parameter.getType() == MultipartFile.class) {
arg[i] = file;
} else if (BaseRequest.class.isAssignableFrom(parameter.getType())) {
arg[i] = MapperUtil.toJava(data, parameter.getType());
}
}
return methodC.invoke(ajax, arg);
} catch (BeansException e) {
e.printStackTrace();
BaseResponse baseResponse = new BaseResponse();
baseResponse.addError(ErrorType.BUSINESS_ERROR, "未找到对应的目标!");
return baseResponse;
} catch (IllegalAccessException e) {
e.printStackTrace();
BaseResponse baseResponse = new BaseResponse();
baseResponse.addError(ErrorType.BUSINESS_ERROR, "方法执必须公开!");
return baseResponse;
} catch (InvocationTargetException e) {
e.getTargetException().printStackTrace();
BaseResponse baseResponse = new BaseResponse();
baseResponse.addError(ErrorType.BUSINESS_ERROR, "方法执行错误[" + e.getTargetException().getMessage() + "]");
return baseResponse;
}
}
@RequestMapping(path = "/api/{module}/{target}/{method}", method = RequestMethod.POST)
@ResponseBody
public String api(
@PathVariable String module,
@PathVariable String target,
@PathVariable String method,
@RequestParam(required = false) String appKey,
@RequestParam(required = false) String sign,
@RequestParam(required = false) Long timestamp,
@RequestParam(required = false) String token,
@RequestParam(required = false) String encryptData,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
BaseResponse response = new BaseResponse();
if (appKey == null) {
response.addError(ErrorType.BUSINESS_ERROR, "应用码参数[appKey]不存在!");
return MapperUtil.toJson(response);
} else if (sign == null) {
response.addError(ErrorType.BUSINESS_ERROR, "签名参数[sign]不存在!");
return MapperUtil.toJson(response);
} else if (timestamp == null) {
response.addError(ErrorType.BUSINESS_ERROR, "时间戳参数[timestamp]不存在!");
return MapperUtil.toJson(response);
}
String data = null;
String appSecret = "1234567890123456";
// 解码
try {
data = AESUtil.decrypt2String(encryptData, appSecret);
} catch (Exception e) {
response.addError(ErrorType.BUSINESS_ERROR, "解码失败,请确认编码是否正确!");
return MapperUtil.toJson(response);
}
// 验证签名
String sign_ = MD5Util.encode(appSecret + data + timestamp);
if (!sign_.equals(sign)) {
response.addError(ErrorType.BUSINESS_ERROR, "签名验证失败!");
return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret);
}
// 时效性验证
long currentTime = System.currentTimeMillis();
if (currentTime - timestamp > 2 * 60 * 1000) {
response.addError(ErrorType.BUSINESS_ERROR, "请求过期, 或本地时间错误!");
return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret);
}
// 权限验证
if (!LocalData.getToken().hasRes(httpServletRequest.getServletPath())) {
response.addError(ErrorType.BUSINESS_ERROR, "[" + httpServletRequest.getServletPath() + "]未授权的资源!");
return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret);
}
// 开始处理业务
try {
String beanClassName = (ActionConfig.API_PREFIX + module + "/" + target).toLowerCase();
Object ajax = LocalData.getApplicationContext().getBean(beanClassName);
Class ajaxClass = ajax.getClass();
Method[] methods = ajaxClass.getDeclaredMethods();
Method methodC = null;
for (Method meth : methods) {
if (meth.getName().equals(method)) {
methodC = meth;
}
}
if (methodC == null) {
response.addError(ErrorType.BUSINESS_ERROR, "未找到对应的方法!");
return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret);
}
Parameter[] parameters = methodC.getParameters();
Object[] arg = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
if (parameter.getType() == HttpServletRequest.class) {
arg[i] = httpServletRequest;
} else if (parameter.getType() == HttpServletResponse.class) {
arg[i] = httpServletResponse;
} else if (BaseRequest.class.isAssignableFrom(parameter.getType())) {
arg[i] = MapperUtil.toJava(data, parameter.getType());
} else if (parameter.getType() == TreeNode.class) {
arg[i] = MapperUtil.toTree(data);
}
}
response = (BaseResponse) methodC.invoke(ajax, arg);
} catch (BeansException e) {
e.printStackTrace();
response.addError(ErrorType.BUSINESS_ERROR, "未找到对应的目标!");
} catch (IllegalAccessException e) {
e.printStackTrace();
response.addError(ErrorType.BUSINESS_ERROR, "方法执必须公开!");
} catch (InvocationTargetException e) {
LogUtil.dumpException(e.getTargetException());
e.getTargetException().printStackTrace();
response.addError(ErrorType.BUSINESS_ERROR, "方法执行错误[" + e.getTargetException().getMessage() + "]");
}
return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret);
}
private static ConcurrentHashMap