From 7f023dd7fa060bbbe34491967ebf6bacae06a5c1 Mon Sep 17 00:00:00 2001 From: yangyao Date: Mon, 6 Apr 2020 16:49:04 +0800 Subject: [PATCH 1/3] =?UTF-8?q?1.=E6=96=B0=E5=A2=9E@DateFormat=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E6=94=AF=E6=8C=81=202.=E5=88=A0=E9=99=A4@DownloadResp?= =?UTF-8?q?onse=E6=B3=A8=E8=A7=A3=203.=E5=88=A0=E9=99=A4=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=BC=82=E5=B8=B8=EF=BC=8C=E9=87=87=E7=94=A8actionExc?= =?UTF-8?q?eption=E5=BC=82=E5=B8=B8=204.=E5=88=A0=E9=99=A4=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E7=9A=84rest=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=205.?= =?UTF-8?q?=E6=9B=B4=E6=96=B0rest=E6=B5=8B=E8=AF=95controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../restful/DefaultRestfulErrorRender.java | 109 ---------- .../restful/JbootRestfulManager.java | 43 ++-- .../components/restful/RestfulAction.java | 12 +- .../restful/RestfulErrorRender.java | 35 ---- .../components/restful/RestfulHandler.java | 191 +----------------- .../components/restful/RestfulInvocation.java | 1 - .../components/restful/RestfulUtils.java | 127 +++++++----- ...{DownloadResponse.java => DateFormat.java} | 13 +- .../ParameterNullErrorException.java | 24 --- .../ParameterParseErrorException.java | 39 ---- .../RequestMethodErrorException.java | 39 ---- .../jboot/test/restful/RestfulController.java | 16 +- 12 files changed, 134 insertions(+), 515 deletions(-) delete mode 100644 src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java delete mode 100644 src/main/java/io/jboot/components/restful/RestfulErrorRender.java rename src/main/java/io/jboot/components/restful/annotation/{DownloadResponse.java => DateFormat.java} (36%) delete mode 100644 src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java delete mode 100644 src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java delete mode 100644 src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java diff --git a/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java b/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java deleted file mode 100644 index 1ba7e278..00000000 --- a/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.core.ActionException; -import com.jfinal.log.Log; -import com.jfinal.render.Render; -import com.jfinal.render.RenderException; -import com.jfinal.render.RenderManager; -import io.jboot.components.restful.exception.ParameterNullErrorException; -import io.jboot.components.restful.exception.ParameterParseErrorException; -import io.jboot.components.restful.exception.RequestMethodErrorException; -import io.jboot.web.handler.JbootActionHandler; - -import java.io.Serializable; - -/** - * 默认的restful错误响应处理器 - */ -public class DefaultRestfulErrorRender extends RestfulErrorRender { - - private static final Log log = Log.getLog(JbootActionHandler.class); - - public static class Error implements Serializable { - private String errorClass; - private int code; - private String message; - - public Error(String errorClass, int code, String message) { - this.errorClass = errorClass; - this.code = code; - this.message = message; - } - - public String getErrorClass() { - return errorClass; - } - - public Error setErrorClass(String errorClass) { - this.errorClass = errorClass; - return this; - } - - public int getCode() { - return code; - } - - public Error setCode(int code) { - this.code = code; - return this; - } - - public String getMessage() { - return message; - } - - public Error setMessage(String message) { - this.message = message; - return this; - } - } - - public void render() { - log.error("The restful handler intercepted the error", super.getError()); - Error error = null; - if(super.getError() instanceof ParameterNullErrorException - || super.getError() instanceof ParameterParseErrorException){ - //400 - error = new Error(super.getError().getClass().getName(), - HttpStatus.BAD_REQUEST.value(), super.getError().getMessage()); - } else if(super.getError() instanceof RequestMethodErrorException){ - error = new Error(super.getError().getClass().getName(), - HttpStatus.METHOD_NOT_ALLOWED.value(), super.getError().getMessage()); - } else if(super.getError() instanceof ActionException){ - //解析错误代码 - ActionException actionException = (ActionException)super.getError(); - int errorCode = actionException.getErrorCode(); - String msg = ""; - if (errorCode == 404) { - msg = HttpStatus.NOT_FOUND.getReasonPhrase(); - } else if (errorCode == 400) { - msg = HttpStatus.BAD_REQUEST.getReasonPhrase(); - } else if (errorCode == 401) { - msg = HttpStatus.UNAUTHORIZED.getReasonPhrase(); - } else if (errorCode == 403) { - msg = HttpStatus.FORBIDDEN.getReasonPhrase(); - } else if(errorCode == 405){ - msg = HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase(); - } else { - msg = super.getError().getMessage(); - } - error = new Error(super.getError().getClass().getName(), - errorCode, msg); - } else if(super.getError() instanceof RenderException){ - //500 - error = new Error(super.getError().getClass().getName(), - HttpStatus.INTERNAL_SERVER_ERROR.value(), super.getError().getMessage()); - } else { - //500 - error = new Error(super.getError().getClass().getName(), - HttpStatus.INTERNAL_SERVER_ERROR.value(), super.getError().getMessage()); - } - Render jsonRender = RenderManager.me().getRenderFactory().getJsonRender(error); - jsonRender.setContext(super.request, super.response, super.getAction() == null? "" : super.getAction().getViewPath()); - super.response.setStatus(error.getCode()); - jsonRender.render(); - } - - - -} diff --git a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java index 4a2f1ed5..e850ec54 100644 --- a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java +++ b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java @@ -3,13 +3,14 @@ package io.jboot.components.restful; import com.jfinal.aop.Interceptor; import com.jfinal.aop.InterceptorManager; import com.jfinal.config.Routes; +import com.jfinal.core.ActionException; import com.jfinal.core.Controller; import com.jfinal.core.NotAction; +import com.jfinal.render.RenderManager; import io.jboot.components.restful.annotation.DeleteMapping; import io.jboot.components.restful.annotation.GetMapping; import io.jboot.components.restful.annotation.PostMapping; import io.jboot.components.restful.annotation.PutMapping; -import io.jboot.components.restful.exception.RequestMethodErrorException; import io.jboot.utils.StrUtil; import io.jboot.web.controller.annotation.RequestMapping; @@ -22,6 +23,7 @@ import java.util.Map; public class JbootRestfulManager { public static class Config { + private boolean mappingSupperClass; private String baseViewPath; private Interceptor[] routeInterceptors; @@ -81,7 +83,7 @@ public class JbootRestfulManager { protected static final String SLASH = "/"; - private RestfulErrorRender restfulErrorRender = new DefaultRestfulErrorRender(); + private RenderManager renderManager = RenderManager.me(); public static JbootRestfulManager me() { return me; @@ -177,10 +179,8 @@ public class JbootRestfulManager { } } RestfulAction action = new RestfulAction(baseRequestMapping, actionKey, controllerClass, - method, method.getName(), actionInters, route.getFinalViewPath(config.getBaseViewPath())); + method, method.getName(), actionInters, route.getFinalViewPath(config.getBaseViewPath()), requestMethod); String key = requestMethod + ":" + actionKey; - -// RestfulAction restfulAction = new RestfulAction(action, actionKey, requestMethod); if (restfulActions.put(key, action) != null) { //已经存在指定的key throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); @@ -205,34 +205,25 @@ public class JbootRestfulManager { //先直接获取 RestfulAction restfulAction = restfulActions.get(actionKey); if (restfulAction == null) { - //路径判断 - String[] paths = actionKey.split(":")[1].replace(requestMethod, "").split(SLASH); - for (String _actionKey : restfulActions.keySet()) { - String _requestMethod = _actionKey.split(":")[0]; - String _target = _actionKey.split(":")[1]; - System.out.println("---------> target:"+target+",_target:"+_target+",_requestMethod:"+_requestMethod+",requestMethod:" + requestMethod); - if( target.equals(_target) && !_requestMethod.equals(requestMethod) ){ + String[] paths = target.split(SLASH); + for(Map.Entry entry : restfulActions.entrySet()){ + String _requestMethod = entry.getValue().getRequestMethod(); + String _target = entry.getValue().getActionKey(); + if (target.equals(_target) && !_requestMethod.equals(requestMethod)) { //请求方法不正确 - throw new RequestMethodErrorException(_actionKey, _requestMethod, target, requestMethod); + throw new ActionException(HttpStatus.METHOD_NOT_ALLOWED.value(), + renderManager.getRenderFactory().getErrorRender(HttpStatus.METHOD_NOT_ALLOWED.value()), + "'" + target + "' is specified as a '" + _requestMethod + "' request. '" + requestMethod + "' requests are not supported"); } - String[] _paths = _actionKey.split(":")[1].replace(requestMethod, "").split(SLASH); - if (_actionKey.startsWith(requestMethod) && - _actionKey.contains("{") && _actionKey.contains("}") + String[] _paths = entry.getValue().getActionKey().split(SLASH); + if (entry.getValue().getActionKey().startsWith(requestMethod) && + entry.getValue().getActionKey().contains("{") && entry.getValue().getActionKey().contains("}") && paths.length == _paths.length && RestfulUtils.comparePaths(_paths, paths)) { - restfulAction = restfulActions.get(_actionKey); + restfulAction = entry.getValue(); break; } } } return restfulAction; } - - public RestfulErrorRender getRestfulErrorRender() { - return restfulErrorRender; - } - - public JbootRestfulManager setRestfulErrorRender(RestfulErrorRender restfulErrorRender) { - this.restfulErrorRender = restfulErrorRender; - return this; - } } diff --git a/src/main/java/io/jboot/components/restful/RestfulAction.java b/src/main/java/io/jboot/components/restful/RestfulAction.java index 7c2ccd4e..1178f8c8 100644 --- a/src/main/java/io/jboot/components/restful/RestfulAction.java +++ b/src/main/java/io/jboot/components/restful/RestfulAction.java @@ -8,7 +8,17 @@ import java.lang.reflect.Method; public class RestfulAction extends Action { - public RestfulAction(String controllerKey, String actionKey, Class controllerClass, Method method, String methodName, Interceptor[] interceptors, String viewPath) { + private String requestMethod; + + public String getRequestMethod() { + return requestMethod; + } + + public RestfulAction(String controllerKey, String actionKey, Class controllerClass, + Method method, String methodName, Interceptor[] interceptors, String viewPath, String requestMethod) { super(controllerKey, actionKey, controllerClass, method, methodName, interceptors, viewPath); + this.requestMethod = requestMethod; } + + } diff --git a/src/main/java/io/jboot/components/restful/RestfulErrorRender.java b/src/main/java/io/jboot/components/restful/RestfulErrorRender.java deleted file mode 100644 index d2154da2..00000000 --- a/src/main/java/io/jboot/components/restful/RestfulErrorRender.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.core.Action; -import com.jfinal.render.Render; - - -public abstract class RestfulErrorRender extends Render { - - private String target; - - private Action action; - - private Exception error; - - public void init (String target, Action action, Exception error) { - this.target = target; - this.action = action; - this.error = error; - } - - protected RestfulErrorRender() { - } - - protected String getTarget() { - return target; - } - - protected Action getAction() { - return action; - } - - protected Exception getError() { - return error; - } -} diff --git a/src/main/java/io/jboot/components/restful/RestfulHandler.java b/src/main/java/io/jboot/components/restful/RestfulHandler.java index a7637393..d6b44370 100644 --- a/src/main/java/io/jboot/components/restful/RestfulHandler.java +++ b/src/main/java/io/jboot/components/restful/RestfulHandler.java @@ -20,14 +20,15 @@ public class RestfulHandler extends JbootActionHandler { @Override public Action getAction(String target, String[] urlPara, HttpServletRequest request) { Action action = super.getAction(target, urlPara); - if (action == null) { + if(action != null){ if (action.getActionKey().equals("/") && action.getMethodName().equals("index") && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { action = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); } } - - + if (action == null) { + action = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); + } return action; } @@ -42,161 +43,6 @@ public class RestfulHandler extends JbootActionHandler { } } - - // @Override -// public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { -// if (target.indexOf('.') != -1) { -// return; -// } -// -// isHandled[0] = true; -// String[] urlPara = {null}; -// Action action = getAction(target, urlPara); -// RestfulAction restfulAction = null; -// if (action != null && action.getActionKey().equals("/") && action.getMethodName().equals("index") -// && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { -// //如果被默认的"/"拦截,并且为index方法,但是请求的url又和actionKey不匹配,则有可能是restful请求 -// try { -// restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); -// } catch (RequestMethodErrorException e) { -// handleActionException(target, request, response, action, e); -// return; -// } -// if (restfulAction != null) { -// action = null; -// } -// } -// //如果无法从内置的action中获取action则尝试从restful管理的action中获取 -// if (action == null) { -// // 尝试从restful 获取action -// try { -// if (restfulAction == null) { -// restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); -// } -// } catch (RequestMethodErrorException e) { -// handleActionException(target, request, response, action, e); -// return; -// } -// if (restfulAction != null) { -// //restful 风格的请求处理 -// restfulRequest(restfulAction, target, request, response, isHandled); -// return; -// } -// if (log.isWarnEnabled()) { -// String qs = request.getQueryString(); -// log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); -// } -// renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); -// return; -// } -// //在获取到了 -// super.handle(target, request, response, isHandled); -// } - -// private void restfulRequest(RestfulAction restfulAction, String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { -// Action action = restfulAction.getAction(); -// Controller controller = null; -// try { -// controller = controllerFactory.getController(action.getControllerClass()); -// //注入依赖 -// if (injectDependency) { -// com.jfinal.aop.Aop.inject(controller); -// } -// //绑定controller本身到当前线程 -// JbootControllerContext.hold(controller); -// //初始化controller -// CPI._init_(controller, action, request, response, null); -// -// //解析参数 -// Object[] args = RestfulUtils.parseActionMethodParameters(target, restfulAction.getActionKey(), -// action.getMethod(), request, controller.getRawData()); -// RestfulCallback restfulCallback = new RestfulCallback(restfulAction, controller); -// -// Invocation invocation = new Invocation(controller, action.getMethod(), action.getInterceptors(), -// restfulCallback, args); -// -// RestfulInvocation fixedInvocation; -// if (devMode) { -// if (ActionReporter.isReportAfterInvocation(request)) { -// fixedInvocation = invokeInvocation(invocation); -// ActionReporter.report(target, controller, action); -// } else { -// ActionReporter.report(target, controller, action); -// fixedInvocation = invokeInvocation(invocation); -// } -// } else { -// fixedInvocation = invokeInvocation(invocation); -// } -// -// Object returnValue = fixedInvocation.getReturnValue(); -// -// // 判断是否带有@DownloadResponse -// DownloadResponse downloadResponse = action.getMethod().getAnnotation(DownloadResponse.class); -// if (returnValue == null && downloadResponse == null) { -// //无返回结果的 restful 请求 -// controller.renderNull(); -// } -// // 如果标记了@DownloadResponse,并且返回值不为空,会被认为自行处理了下载行为 -// if (downloadResponse != null) { -// return; -// } -// if (returnValue != null) { -// //初始化返回值 -// initRenderValue(returnValue, controller); -// } -// -// Render render = controller.getRender(); -// if (render instanceof ForwardActionRender) { -// String actionUrl = ((ForwardActionRender) render).getActionUrl(); -// if (target.equals(actionUrl)) { -// throw new RuntimeException("The forward action url is the same as before."); -// } else { -// handle(actionUrl, request, response, isHandled); -// } -// return; -// } -// -// if (render == null) { -// render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); -// } -// -// //初始化自定义头部 -// initResponseHeaders(response, fixedInvocation.getMethod()); -// //响应开始 -// render.setContext(request, response, action.getViewPath()).render(); -// -// } catch (RenderException | ParameterNullErrorException | ParameterParseErrorException | ActionException e) { -// handleActionException(target, request, response, action, e); -// } catch (Exception e) { -// String info = ClassUtil.buildMethodString(action.getMethod()); -// if (log.isErrorEnabled()) { -// String qs = request.getQueryString(); -// String targetInfo = qs == null ? target : target + "?" + qs; -// log.error(info + " : " + targetInfo, e); -// } -// //自定义错误处理 -// handleActionException(target, request, response, action, e); -// } finally { -// JbootControllerContext.release(); -// controllerFactory.recycle(controller); -// } -// } - -// private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, -// Action action, Exception e) { -// RestfulErrorRender restfulErrorRender = JbootRestfulManager.me().getRestfulErrorRender(); -// restfulErrorRender.setContext(request, response, action == null ? "" : action.getViewPath()); -// restfulErrorRender.init(target, action, e); -// restfulErrorRender.render(); -// } - - -// protected RestfulInvocation invokeInvocation(Invocation inv) { -// RestfulInvocation fixedInvocation = new RestfulInvocation(inv); -// fixedInvocation.invoke(); -// return fixedInvocation; -// } - @Override public void setResponse(HttpServletResponse response, Action action) { ResponseHeader[] responseHeaders = action.getMethod().getAnnotationsByType(ResponseHeader.class); @@ -215,33 +61,4 @@ public class RestfulHandler extends JbootActionHandler { } } -// protected void initRenderValue(Object o, Controller controller) { -// if (o.getClass().equals(String.class) -// || o.getClass().equals(int.class) -// || o.getClass().equals(double.class) -// || o.getClass().equals(byte.class) -// || o.getClass().equals(long.class) -// || o.getClass().equals(float.class) -// || o.getClass().equals(short.class) -// || o.getClass().equals(char.class) -// || o.getClass().equals(boolean.class)) { -// controller.renderText(String.valueOf(o)); -// } else if (o.getClass().equals(File.class)) { -// controller.renderFile((File) o); -// } else if (o.getClass().equals(ResponseEntity.class)) { -// ResponseEntity responseEntity = (ResponseEntity) o; -// //设置自定义头部信息 -// Map headers = responseEntity.getHeaders(); -// -// headers.forEach((k, v) -> controller.getResponse().setHeader(k, v)); -// //设置http状态代码 -// controller.getResponse().setStatus(responseEntity.getHttpStatus().value()); -// initRenderValue(responseEntity.getData(), controller); -// } else if (o instanceof Render) { //如果是render类型直接设置render -// controller.render((Render) o); -// } else { -// controller.renderJson(o); -// } -// } - } diff --git a/src/main/java/io/jboot/components/restful/RestfulInvocation.java b/src/main/java/io/jboot/components/restful/RestfulInvocation.java index fe0df0d6..99e16b58 100644 --- a/src/main/java/io/jboot/components/restful/RestfulInvocation.java +++ b/src/main/java/io/jboot/components/restful/RestfulInvocation.java @@ -8,7 +8,6 @@ public class RestfulInvocation extends Invocation { private Action action; - public RestfulInvocation(Action action, Controller controller, Object[] args) { super(controller, action.getMethod(), action.getInterceptors(), new RestfulCallback(action, controller), args); this.action = action; diff --git a/src/main/java/io/jboot/components/restful/RestfulUtils.java b/src/main/java/io/jboot/components/restful/RestfulUtils.java index cbb64f62..e9190698 100644 --- a/src/main/java/io/jboot/components/restful/RestfulUtils.java +++ b/src/main/java/io/jboot/components/restful/RestfulUtils.java @@ -1,23 +1,27 @@ package io.jboot.components.restful; +import com.jfinal.core.ActionException; import com.jfinal.kit.JsonKit; -import io.jboot.components.restful.annotation.PathVariable; -import io.jboot.components.restful.annotation.RequestBody; -import io.jboot.components.restful.annotation.RequestHeader; -import io.jboot.components.restful.annotation.RequestParam; -import io.jboot.components.restful.exception.ParameterNullErrorException; -import io.jboot.components.restful.exception.ParameterParseErrorException; +import com.jfinal.render.RenderManager; +import io.jboot.components.restful.annotation.*; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.lang.reflect.Parameter; +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.TimeZone; public class RestfulUtils { + private static final RenderManager renderManager = RenderManager.me(); + /** * 从url中解析路径参数 * @@ -52,11 +56,10 @@ public class RestfulUtils { * @param request * @param rawData * @return - * @throws ParameterNullErrorException - * @throws ParameterParseErrorException + * @throws ActionException */ public static Object[] parseActionMethodParameters(String target, String actionKey, Method actionMethod, HttpServletRequest request, String rawData) - throws ParameterNullErrorException, ParameterParseErrorException { + throws ActionException { Object[] args = new Object[actionMethod.getParameters().length]; for (int i = 0; i < actionMethod.getParameters().length; i++) { Parameter parameter = actionMethod.getParameters()[i]; @@ -72,28 +75,28 @@ public class RestfulUtils { } values = request.getParameterValues(parameterName); parameter.getType(); - args[i] = parseRequestParamToParameter(values, parameterName, parameter.getType()); + args[i] = parseRequestParamToParameter(values, parameterName, parameter.getType(), parameter); if (args[i] == null && requestParam.required()) { - //要求参数为空,但是却并没有提供参数 - throw new ParameterNullErrorException(parameterName); + //要求参数不为空,但是却并没有提供参数 + throw genBindError("Parameter '" + parameterName + "' specifies a forced check, but the value is null"); } } else if (requestBody != null) { - args[i] = parseRequestBodyToParameter(rawData, parameterName, parameter.getType()); + args[i] = parseRequestBodyToParameter(rawData, parameterName, parameter.getType(), parameter); } else if (requestHeader != null) { if (StrUtil.isNotBlank(requestHeader.value())) { parameterName = requestHeader.value(); } String value = request.getHeader(parameterName); - args[i] = parseRequestHeaderToParameter(value, parameterName, parameter.getType()); + args[i] = parseRequestHeaderToParameter(value, parameterName, parameter.getType(), parameter); if (args[i] == null && requestHeader.required()) { - //要求参数为空,但是却并没有提供参数 - throw new ParameterNullErrorException(parameterName); + //要求参数不为空,但是却并没有提供参数 + throw genBindError("Parameter '" + parameterName + "' specifies a forced check, but the value is null"); } } else if (pathVariable != null) { if (StrUtil.isNotBlank(pathVariable.value())) { parameterName = pathVariable.value(); } - args[i] = parsePathVariableToParameter(target, actionKey, parameterName, parameter.getType()); + args[i] = parsePathVariableToParameter(target, actionKey, parameterName, parameter.getType(), parameter); } else { args[i] = null; } @@ -119,83 +122,107 @@ public class RestfulUtils { return matchingCount == sourcePaths.length; } - private static Object parseRequestParamToParameter(String[] value, String name, Class parameterTypeClass) { - if(parameterTypeClass.isArray()){ - Object [] objects = new Object[value.length]; + private static Object parseRequestParamToParameter(String[] value, String name, Class parameterTypeClass, Parameter parameter) { + if (parameterTypeClass.isArray()) { + Object[] objects = new Object[value.length]; for (int i = 0; i < value.length; i++) { - objects[i] = parseCommonValue(value[i], name, parameterTypeClass); + objects[i] = parseCommonValue(value[i], name, parameterTypeClass, parameter); } return objects; } else { - if(value != null && value.length > 0){ - return parseCommonValue(value[0], name, parameterTypeClass); + if (value != null && value.length > 0) { + return parseCommonValue(value[0], name, parameterTypeClass, parameter); } } return null; } - private static Object parseRequestHeaderToParameter(String header, String name, Class parameterTypeClass) { - return parseCommonValue(header, name, parameterTypeClass); + private static Object parseRequestHeaderToParameter(String header, String name, Class parameterTypeClass, Parameter parameter) { + return parseCommonValue(header, name, parameterTypeClass, parameter); } - private static Object parseRequestBodyToParameter(String body, String name, Class parameterTypeClass) { + private static Object parseRequestBodyToParameter(String body, String name, Class parameterTypeClass, Parameter parameter) { //先当作基本数据来转换 - Object value = parseCommonValue(body, name, parameterTypeClass); - if(value == null){ + Object value = parseCommonValue(body, name, parameterTypeClass, parameter); + if (value == null) { value = JsonKit.parse(body, parameterTypeClass); } return value; } - private static Object parsePathVariableToParameter(String target, String actionKey, String parameterName, Class parameterTypeClass) { + private static Object parsePathVariableToParameter(String target, String actionKey, String parameterName, Class parameterTypeClass, Parameter parameter) { Map pathVariables = parsePathVariables(target, actionKey); String value = pathVariables.get(parameterName); - return parseCommonValue(value, parameterName, parameterTypeClass); + return parseCommonValue(value, parameterName, parameterTypeClass, parameter); } /** * 转换基本类型参数,目前支持string,int,double,float,boolean,long基本类型数据 + * * @param value * @param name * @param parameterTypeClass + * @param parameter * @return */ - private static Object parseCommonValue(String value, String name, Class parameterTypeClass) { + private static Object parseCommonValue(String value, String name, Class parameterTypeClass, Parameter parameter) { if (StrUtil.isBlank(value)) { return null; } if (parameterTypeClass.equals(String.class)) { return value; - } else if (parameterTypeClass.equals(int.class)) { - try { - return Integer.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); - } - } else if (parameterTypeClass.equals(double.class)) { - try { - return Double.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); - } - } else if (parameterTypeClass.equals(float.class)) { + } else if (parameterTypeClass.equals(int.class) + || parameterTypeClass.equals(double.class) + || parameterTypeClass.equals(float.class) + || parameterTypeClass.equals(long.class) + || parameterTypeClass.equals(BigDecimal.class) + || parameterTypeClass.equals(short.class)) { try { - return Float.valueOf(value); + if (parameterTypeClass.equals(int.class)) { + return Integer.valueOf(value); + } else if (parameterTypeClass.equals(double.class)) { + return Double.valueOf(value); + } else if (parameterTypeClass.equals(float.class)) { + return Float.valueOf(value); + } else if (parameterTypeClass.equals(long.class)) { + return Long.valueOf(value); + } else if (parameterTypeClass.equals(BigDecimal.class)) { + return new BigDecimal(value); + } else if (parameterTypeClass.equals(short.class)) { + return Short.valueOf(value); + } else { + return null; + } } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); + throw genBindError("Error resolving parameter '" + name + "', unable to match value '" + + value + "' to specified type '" + parameterTypeClass.getName() + "'"); } } else if (parameterTypeClass.equals(boolean.class)) { return Boolean.valueOf(value); - } else if (parameterTypeClass.equals(long.class)) { + } else if (parameterTypeClass.equals(Date.class)) { + DateFormat dateFormat = parameter.getAnnotation(DateFormat.class); + SimpleDateFormat simpleDateFormat; + if (dateFormat != null) { + simpleDateFormat = new SimpleDateFormat(dateFormat.value()); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone(dateFormat.timeZone())); + } else { + simpleDateFormat = new SimpleDateFormat(); + } try { - return Long.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); + return simpleDateFormat.parse(value); + } catch (ParseException e) { + throw genBindError("Error resolving parameter '" + name + "', unable to match value '" + + value + "' to specified type '" + parameterTypeClass.getName() + "'"); } } else { return null; } } + private static ActionException genBindError(String message) { + return new ActionException(HttpStatus.BAD_REQUEST.value(), + renderManager.getRenderFactory().getErrorRender(HttpStatus.BAD_REQUEST.value()), message); + } + } diff --git a/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java b/src/main/java/io/jboot/components/restful/annotation/DateFormat.java similarity index 36% rename from src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java rename to src/main/java/io/jboot/components/restful/annotation/DateFormat.java index c4aa83e8..b8939ecf 100644 --- a/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java +++ b/src/main/java/io/jboot/components/restful/annotation/DateFormat.java @@ -3,10 +3,17 @@ package io.jboot.components.restful.annotation; import java.lang.annotation.*; /** - * 标注controller action是一个下载响应,并且需要action自行处理response + * + * 日期注解,可以结合@PathVarible,@RequestParam,@RequestHeader一起使用 + * */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target(ElementType.PARAMETER) @Documented -public @interface DownloadResponse { +public @interface DateFormat { + + String value() default "yyyy-MM-dd HH:mm:ss"; + + String timeZone() default "GMT+8"; + } diff --git a/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java b/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java deleted file mode 100644 index 081867cc..00000000 --- a/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 参数为空错误 - */ -public class ParameterNullErrorException extends RuntimeException { - - private String parameterName; - - - public String getParameterName() { - return parameterName; - } - - public ParameterNullErrorException(String parameterName) { - super("Parameter '"+parameterName+"' specifies a forced check, but the value is null"); - this.parameterName = parameterName; - } - - public ParameterNullErrorException(Exception e) { - super(e); - } - -} diff --git a/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java b/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java deleted file mode 100644 index 2a025ab6..00000000 --- a/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 参数类型错误 - */ -public class ParameterParseErrorException extends RuntimeException { - - - private String parameterValue; - - private String parameterName; - - private Class parameterType; - - public String getParameterValue() { - return parameterValue; - } - - public String getParameterName() { - return parameterName; - } - - public Class getParameterType() { - return parameterType; - } - - public ParameterParseErrorException(String parameterValue, String parameterName, Class parameterType) { - super("Error resolving parameter '" + parameterName + "', unable to match value '" - + parameterValue + "' to specified type '" + parameterType.getName() + "'"); - this.parameterValue = parameterValue; - this.parameterName = parameterName; - this.parameterType = parameterType; - } - - public ParameterParseErrorException(Exception e) { - super(e); - } - -} diff --git a/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java b/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java deleted file mode 100644 index e72f83c9..00000000 --- a/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 请求方法错误 - */ -public class RequestMethodErrorException extends RuntimeException { - - private String actionKey; - - private String actionMethod; - - private String target; - - private String targetMethod; - - public String getActionKey() { - return actionKey; - } - - public String getActionMethod() { - return actionMethod; - } - - public String getTarget() { - return target; - } - - public String getTargetMethod() { - return targetMethod; - } - - public RequestMethodErrorException(String actionKey, String actionMethod, String target, String targetMethod) { - super("'" + target + "' is specified as a '" + actionMethod + "' request. '" + targetMethod + "' requests are not supported"); - this.actionKey = actionKey; - this.actionMethod = actionMethod; - this.target = target; - this.targetMethod = targetMethod; - } -} diff --git a/src/test/java/io/jboot/test/restful/RestfulController.java b/src/test/java/io/jboot/test/restful/RestfulController.java index 08a2367d..638ccf46 100644 --- a/src/test/java/io/jboot/test/restful/RestfulController.java +++ b/src/test/java/io/jboot/test/restful/RestfulController.java @@ -15,7 +15,9 @@ import io.jboot.web.controller.annotation.RequestMapping; import io.jboot.web.cors.EnableCORS; import java.io.Serializable; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; @RestController @@ -113,5 +115,17 @@ public class RestfulController extends JbootController { System.out.println("delete by name : " + name); System.out.println("get token header : " + token); } - + + @GetMapping("/now-date") + public Date nowDate(){ + return new Date(); + } + + @PutMapping("/input-date") + public void inputDate(@RequestParam(value = "date", required = true) @DateFormat Date date){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + System.out.println("get input date: " + sdf.format(date)); + renderNull(); + } + } -- Gitee From dbc73f3f87fa19360d3a9b7c5213e4a24f10c0bc Mon Sep 17 00:00:00 2001 From: yangyao Date: Mon, 6 Apr 2020 17:33:43 +0800 Subject: [PATCH 2/3] =?UTF-8?q?1.=E6=96=B0=E5=A2=9E@DateFormat=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E6=94=AF=E6=8C=81=202.=E5=88=A0=E9=99=A4@DownloadResp?= =?UTF-8?q?onse=E6=B3=A8=E8=A7=A3=203.=E5=88=A0=E9=99=A4=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=BC=82=E5=B8=B8=EF=BC=8C=E9=87=87=E7=94=A8actionExc?= =?UTF-8?q?eption=E5=BC=82=E5=B8=B8=204.=E5=88=A0=E9=99=A4=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E7=9A=84rest=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=205.?= =?UTF-8?q?=E6=9B=B4=E6=96=B0rest=E6=B5=8B=E8=AF=95controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/components/restful/HttpStatus.java | 405 ++++++++++++++++++ .../restful/JbootRestfulManager.java | 229 ++++++++++ .../components/restful/ResponseEntity.java | 67 +++ .../components/restful/RestfulAction.java | 24 ++ .../components/restful/RestfulCallback.java | 21 + .../components/restful/RestfulHandler.java | 64 +++ .../components/restful/RestfulInvocation.java | 56 +++ .../components/restful/RestfulUtils.java | 228 ++++++++++ .../restful/annotation/DeleteMapping.java | 19 + .../restful/annotation/GetMapping.java | 16 + .../restful/annotation/PathVariable.java | 30 ++ .../restful/annotation/PostMapping.java | 16 + .../restful/annotation/PutMapping.java | 19 + .../restful/annotation/RequestBody.java | 23 + .../restful/annotation/RequestHeader.java | 31 ++ .../restful/annotation/RequestParam.java | 31 ++ .../restful/annotation/ResponseHeader.java | 17 + .../restful/annotation/ResponseHeaders.java | 12 + .../restful/annotation/RestController.java | 12 + .../java/io/jboot/core/JbootCoreConfig.java | 32 +- .../web/render/JbootReturnValueRender.java | 13 +- .../jboot/test/restful/RestfulController.java | 138 ++++++ .../io/jboot/test/restful/RestfulService.java | 5 + .../test/restful/RestfulServiceImpl.java | 12 + 24 files changed, 1518 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/jboot/components/restful/HttpStatus.java create mode 100644 src/main/java/io/jboot/components/restful/JbootRestfulManager.java create mode 100644 src/main/java/io/jboot/components/restful/ResponseEntity.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulAction.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulCallback.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulHandler.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulInvocation.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulUtils.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/GetMapping.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/PathVariable.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/PostMapping.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/PutMapping.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestBody.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestHeader.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestParam.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/RestController.java create mode 100644 src/test/java/io/jboot/test/restful/RestfulController.java create mode 100644 src/test/java/io/jboot/test/restful/RestfulService.java create mode 100644 src/test/java/io/jboot/test/restful/RestfulServiceImpl.java diff --git a/src/main/java/io/jboot/components/restful/HttpStatus.java b/src/main/java/io/jboot/components/restful/HttpStatus.java new file mode 100644 index 00000000..aa54f020 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/HttpStatus.java @@ -0,0 +1,405 @@ +package io.jboot.components.restful; + +public enum HttpStatus { + + /** + * {@code 100 Continue}. + * @see HTTP/1.1: Semantics and Content, section 6.2.1 + */ + CONTINUE(100, "Continue"), + /** + * {@code 101 Switching Protocols}. + * @see HTTP/1.1: Semantics and Content, section 6.2.2 + */ + SWITCHING_PROTOCOLS(101, "Switching Protocols"), + /** + * {@code 102 Processing}. + * @see WebDAV + */ + PROCESSING(102, "Processing"), + /** + * {@code 103 Checkpoint}. + * @see A proposal for supporting + * resumable POST/PUT HTTP requests in HTTP/1.0 + */ + CHECKPOINT(103, "Checkpoint"), + + // 2xx Success + + /** + * {@code 200 OK}. + * @see HTTP/1.1: Semantics and Content, section 6.3.1 + */ + OK(200, "OK"), + /** + * {@code 201 Created}. + * @see HTTP/1.1: Semantics and Content, section 6.3.2 + */ + CREATED(201, "Created"), + /** + * {@code 202 Accepted}. + * @see HTTP/1.1: Semantics and Content, section 6.3.3 + */ + ACCEPTED(202, "Accepted"), + /** + * {@code 203 Non-Authoritative Information}. + * @see HTTP/1.1: Semantics and Content, section 6.3.4 + */ + NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"), + /** + * {@code 204 No Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.5 + */ + NO_CONTENT(204, "No Content"), + /** + * {@code 205 Reset Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.6 + */ + RESET_CONTENT(205, "Reset Content"), + /** + * {@code 206 Partial Content}. + * @see HTTP/1.1: Range Requests, section 4.1 + */ + PARTIAL_CONTENT(206, "Partial Content"), + /** + * {@code 207 Multi-Status}. + * @see WebDAV + */ + MULTI_STATUS(207, "Multi-Status"), + /** + * {@code 208 Already Reported}. + * @see WebDAV Binding Extensions + */ + ALREADY_REPORTED(208, "Already Reported"), + /** + * {@code 226 IM Used}. + * @see Delta encoding in HTTP + */ + IM_USED(226, "IM Used"), + + // 3xx Redirection + + /** + * {@code 300 Multiple Choices}. + * @see HTTP/1.1: Semantics and Content, section 6.4.1 + */ + MULTIPLE_CHOICES(300, "Multiple Choices"), + /** + * {@code 301 Moved Permanently}. + * @see HTTP/1.1: Semantics and Content, section 6.4.2 + */ + MOVED_PERMANENTLY(301, "Moved Permanently"), + /** + * {@code 302 Found}. + * @see HTTP/1.1: Semantics and Content, section 6.4.3 + */ + FOUND(302, "Found"), + /** + * {@code 302 Moved Temporarily}. + * @see HTTP/1.0, section 9.3 + * @deprecated in favor of {@link #FOUND} which will be returned from {@code HttpStatus.valueOf(302)} + */ + @Deprecated + MOVED_TEMPORARILY(302, "Moved Temporarily"), + /** + * {@code 303 See Other}. + * @see HTTP/1.1: Semantics and Content, section 6.4.4 + */ + SEE_OTHER(303, "See Other"), + /** + * {@code 304 Not Modified}. + * @see HTTP/1.1: Conditional Requests, section 4.1 + */ + NOT_MODIFIED(304, "Not Modified"), + /** + * {@code 305 Use Proxy}. + * @see HTTP/1.1: Semantics and Content, section 6.4.5 + * @deprecated due to security concerns regarding in-band configuration of a proxy + */ + @Deprecated + USE_PROXY(305, "Use Proxy"), + /** + * {@code 307 Temporary Redirect}. + * @see HTTP/1.1: Semantics and Content, section 6.4.7 + */ + TEMPORARY_REDIRECT(307, "Temporary Redirect"), + /** + * {@code 308 Permanent Redirect}. + * @see RFC 7238 + */ + PERMANENT_REDIRECT(308, "Permanent Redirect"), + + // --- 4xx Client Error --- + + /** + * {@code 400 Bad Request}. + * @see HTTP/1.1: Semantics and Content, section 6.5.1 + */ + BAD_REQUEST(400, "Bad Request"), + /** + * {@code 401 Unauthorized}. + * @see HTTP/1.1: Authentication, section 3.1 + */ + UNAUTHORIZED(401, "Unauthorized"), + /** + * {@code 402 Payment Required}. + * @see HTTP/1.1: Semantics and Content, section 6.5.2 + */ + PAYMENT_REQUIRED(402, "Payment Required"), + /** + * {@code 403 Forbidden}. + * @see HTTP/1.1: Semantics and Content, section 6.5.3 + */ + FORBIDDEN(403, "Forbidden"), + /** + * {@code 404 Not Found}. + * @see HTTP/1.1: Semantics and Content, section 6.5.4 + */ + NOT_FOUND(404, "Not Found"), + /** + * {@code 405 Method Not Allowed}. + * @see HTTP/1.1: Semantics and Content, section 6.5.5 + */ + METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + /** + * {@code 406 Not Acceptable}. + * @see HTTP/1.1: Semantics and Content, section 6.5.6 + */ + NOT_ACCEPTABLE(406, "Not Acceptable"), + /** + * {@code 407 Proxy Authentication Required}. + * @see HTTP/1.1: Authentication, section 3.2 + */ + PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), + /** + * {@code 408 Request Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.5.7 + */ + REQUEST_TIMEOUT(408, "Request Timeout"), + /** + * {@code 409 Conflict}. + * @see HTTP/1.1: Semantics and Content, section 6.5.8 + */ + CONFLICT(409, "Conflict"), + /** + * {@code 410 Gone}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.9 + */ + GONE(410, "Gone"), + /** + * {@code 411 Length Required}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.10 + */ + LENGTH_REQUIRED(411, "Length Required"), + /** + * {@code 412 Precondition failed}. + * @see + * HTTP/1.1: Conditional Requests, section 4.2 + */ + PRECONDITION_FAILED(412, "Precondition Failed"), + /** + * {@code 413 Payload Too Large}. + * @since 4.1 + * @see + * HTTP/1.1: Semantics and Content, section 6.5.11 + */ + PAYLOAD_TOO_LARGE(413, "Payload Too Large"), + /** + * {@code 413 Request Entity Too Large}. + * @see HTTP/1.1, section 10.4.14 + * @deprecated in favor of {@link #PAYLOAD_TOO_LARGE} which will be + * returned from {@code HttpStatus.valueOf(413)} + */ + @Deprecated + REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"), + /** + * {@code 414 URI Too Long}. + * @since 4.1 + * @see + * HTTP/1.1: Semantics and Content, section 6.5.12 + */ + URI_TOO_LONG(414, "URI Too Long"), + /** + * {@code 414 Request-URI Too Long}. + * @see HTTP/1.1, section 10.4.15 + * @deprecated in favor of {@link #URI_TOO_LONG} which will be returned from {@code HttpStatus.valueOf(414)} + */ + @Deprecated + REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), + /** + * {@code 415 Unsupported Media Type}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.13 + */ + UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + /** + * {@code 416 Requested Range Not Satisfiable}. + * @see HTTP/1.1: Range Requests, section 4.4 + */ + REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable"), + /** + * {@code 417 Expectation Failed}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.14 + */ + EXPECTATION_FAILED(417, "Expectation Failed"), + /** + * {@code 418 I'm a teapot}. + * @see HTCPCP/1.0 + */ + I_AM_A_TEAPOT(418, "I'm a teapot"), + /** + * @deprecated See + * + * WebDAV Draft Changes + */ + @Deprecated + INSUFFICIENT_SPACE_ON_RESOURCE(419, "Insufficient Space On Resource"), + /** + * @deprecated See + * + * WebDAV Draft Changes + */ + @Deprecated + METHOD_FAILURE(420, "Method Failure"), + /** + * @deprecated + * See + * WebDAV Draft Changes + */ + @Deprecated + DESTINATION_LOCKED(421, "Destination Locked"), + /** + * {@code 422 Unprocessable Entity}. + * @see WebDAV + */ + UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"), + /** + * {@code 423 Locked}. + * @see WebDAV + */ + LOCKED(423, "Locked"), + /** + * {@code 424 Failed Dependency}. + * @see WebDAV + */ + FAILED_DEPENDENCY(424, "Failed Dependency"), + /** + * {@code 426 Upgrade Required}. + * @see Upgrading to TLS Within HTTP/1.1 + */ + UPGRADE_REQUIRED(426, "Upgrade Required"), + /** + * {@code 428 Precondition Required}. + * @see Additional HTTP Status Codes + */ + PRECONDITION_REQUIRED(428, "Precondition Required"), + /** + * {@code 429 Too Many Requests}. + * @see Additional HTTP Status Codes + */ + TOO_MANY_REQUESTS(429, "Too Many Requests"), + /** + * {@code 431 Request Header Fields Too Large}. + * @see Additional HTTP Status Codes + */ + REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), + /** + * {@code 451 Unavailable For Legal Reasons}. + * @see + * An HTTP Status Code to Report Legal Obstacles + * @since 4.3 + */ + UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"), + + // --- 5xx Server Error --- + + /** + * {@code 500 Internal Server Error}. + * @see HTTP/1.1: Semantics and Content, section 6.6.1 + */ + INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + /** + * {@code 501 Not Implemented}. + * @see HTTP/1.1: Semantics and Content, section 6.6.2 + */ + NOT_IMPLEMENTED(501, "Not Implemented"), + /** + * {@code 502 Bad Gateway}. + * @see HTTP/1.1: Semantics and Content, section 6.6.3 + */ + BAD_GATEWAY(502, "Bad Gateway"), + /** + * {@code 503 Service Unavailable}. + * @see HTTP/1.1: Semantics and Content, section 6.6.4 + */ + SERVICE_UNAVAILABLE(503, "Service Unavailable"), + /** + * {@code 504 Gateway Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.6.5 + */ + GATEWAY_TIMEOUT(504, "Gateway Timeout"), + /** + * {@code 505 HTTP Version Not Supported}. + * @see HTTP/1.1: Semantics and Content, section 6.6.6 + */ + HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported"), + /** + * {@code 506 Variant Also Negotiates} + * @see Transparent Content Negotiation + */ + VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"), + /** + * {@code 507 Insufficient Storage} + * @see WebDAV + */ + INSUFFICIENT_STORAGE(507, "Insufficient Storage"), + /** + * {@code 508 Loop Detected} + * @see WebDAV Binding Extensions + */ + LOOP_DETECTED(508, "Loop Detected"), + /** + * {@code 509 Bandwidth Limit Exceeded} + */ + BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"), + /** + * {@code 510 Not Extended} + * @see HTTP Extension Framework + */ + NOT_EXTENDED(510, "Not Extended"), + /** + * {@code 511 Network Authentication Required}. + * @see Additional HTTP Status Codes + */ + NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); + + + private final int value; + + private final String reasonPhrase; + + + HttpStatus(int value, String reasonPhrase) { + this.value = value; + this.reasonPhrase = reasonPhrase; + } + + + /** + * Return the integer value of this status code. + */ + public int value() { + return this.value; + } + + /** + * Return the reason phrase of this status code. + */ + public String getReasonPhrase() { + return this.reasonPhrase; + } + +} diff --git a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java new file mode 100644 index 00000000..e850ec54 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java @@ -0,0 +1,229 @@ +package io.jboot.components.restful; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.InterceptorManager; +import com.jfinal.config.Routes; +import com.jfinal.core.ActionException; +import com.jfinal.core.Controller; +import com.jfinal.core.NotAction; +import com.jfinal.render.RenderManager; +import io.jboot.components.restful.annotation.DeleteMapping; +import io.jboot.components.restful.annotation.GetMapping; +import io.jboot.components.restful.annotation.PostMapping; +import io.jboot.components.restful.annotation.PutMapping; +import io.jboot.utils.StrUtil; +import io.jboot.web.controller.annotation.RequestMapping; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class JbootRestfulManager { + + public static class Config { + + private boolean mappingSupperClass; + private String baseViewPath; + private Interceptor[] routeInterceptors; + private List routes; + + public Config() { + } + + public Config(boolean mappingSupperClass, String baseViewPath, + Interceptor[] routeInterceptors, List routes) { + this.mappingSupperClass = mappingSupperClass; + this.baseViewPath = baseViewPath; + this.routeInterceptors = routeInterceptors; + this.routes = routes; + } + + public boolean isMappingSupperClass() { + return mappingSupperClass; + } + + public Config setMappingSupperClass(boolean mappingSupperClass) { + this.mappingSupperClass = mappingSupperClass; + return this; + } + + public String getBaseViewPath() { + return baseViewPath; + } + + public Config setBaseViewPath(String baseViewPath) { + this.baseViewPath = baseViewPath; + return this; + } + + public Interceptor[] getRouteInterceptors() { + return routeInterceptors; + } + + public Config setRouteInterceptors(Interceptor[] routeInterceptors) { + this.routeInterceptors = routeInterceptors; + return this; + } + + public List getRoutes() { + return routes; + } + + public Config setRoutes(List routes) { + this.routes = routes; + return this; + } + } + + private static JbootRestfulManager me = new JbootRestfulManager(); + + private Map restfulActions = new HashMap<>(2048, 0.5F); + + protected static final String SLASH = "/"; + + private RenderManager renderManager = RenderManager.me(); + + public static JbootRestfulManager me() { + return me; + } + + public void init(Config config) { + if (config.getRoutes() == null || config.getRoutes().isEmpty()) { + return; + } + InterceptorManager interMan = InterceptorManager.me(); + Class dc; + // 初始化自定义的restful controller + for (Routes.Route route : config.getRoutes()) { + Class controllerClass = route.getControllerClass(); + Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass); + + boolean declaredMethods = config.isMappingSupperClass() + ? controllerClass.getSuperclass() == Controller.class + : true; + + String baseRequestMapping = SLASH; + if (controllerClass.getAnnotation(RequestMapping.class) != null) { + RequestMapping requestMapping = controllerClass.getAnnotation(RequestMapping.class); + if (requestMapping.value().startsWith(SLASH)) { + baseRequestMapping = requestMapping.value(); + } else { + baseRequestMapping = baseRequestMapping + requestMapping.value(); + } + } + + Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods()); + for (Method method : methods) { + if (declaredMethods) { + if (!Modifier.isPublic(method.getModifiers())) { + continue; + } + } else { + dc = method.getDeclaringClass(); + if (dc == Controller.class || dc == Object.class) { + continue; + } + } + //去除mapping + if (method.getAnnotation(NotAction.class) != null) { + continue; + } + Interceptor[] actionInters = interMan.buildControllerActionInterceptor(config.getRouteInterceptors(), controllerInters, controllerClass, method); + + String actionKey = baseRequestMapping; + + //GET 判断 + GetMapping getMapping = method.getAnnotation(GetMapping.class); + PostMapping postMapping = method.getAnnotation(PostMapping.class); + PutMapping putMapping = method.getAnnotation(PutMapping.class); + DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class); + String requestMethod = "", mappingValue = ""; + if (getMapping != null) { + requestMethod = "GET"; + if (StrUtil.isNotBlank(getMapping.value())) { + mappingValue = getMapping.value(); + } + } else if (postMapping != null) { + requestMethod = "POST"; + if (StrUtil.isNotBlank(postMapping.value())) { + mappingValue = postMapping.value(); + } + } else if (putMapping != null) { + requestMethod = "PUT"; + if (StrUtil.isNotBlank(putMapping.value())) { + mappingValue = putMapping.value(); + } + } else if (deleteMapping != null) { + requestMethod = "DELETE"; + if (StrUtil.isNotBlank(deleteMapping.value())) { + mappingValue = deleteMapping.value(); + } + } else { + //默认为get请求 + requestMethod = "GET"; + mappingValue = SLASH; + } + if (StrUtil.isNotBlank(mappingValue)) { + if (!actionKey.endsWith(SLASH)) { + actionKey = actionKey + SLASH; + } + if (mappingValue.startsWith(SLASH)) { + mappingValue = mappingValue.substring(1); + } + actionKey = actionKey + mappingValue; + } else { + if (actionKey.endsWith(SLASH)) { + actionKey = actionKey.substring(0, actionKey.length() - 1); + } + } + RestfulAction action = new RestfulAction(baseRequestMapping, actionKey, controllerClass, + method, method.getName(), actionInters, route.getFinalViewPath(config.getBaseViewPath()), requestMethod); + String key = requestMethod + ":" + actionKey; + if (restfulActions.put(key, action) != null) { + //已经存在指定的key + throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); + } + } + } + } + + protected String buildMsg(String actionKey, Class controllerClass, Method method) { + StringBuilder sb = new StringBuilder("The action \"") + .append(controllerClass.getName()).append(".") + .append(method.getName()).append("()\" can not be mapped, ") + .append("actionKey \"").append(actionKey).append("\" is already in use."); + + String msg = sb.toString(); + System.err.println("\nException: " + msg); + return msg; + } + + public RestfulAction getRestfulAction(String target, String requestMethod) { + String actionKey = requestMethod + ":" + target; + //先直接获取 + RestfulAction restfulAction = restfulActions.get(actionKey); + if (restfulAction == null) { + String[] paths = target.split(SLASH); + for(Map.Entry entry : restfulActions.entrySet()){ + String _requestMethod = entry.getValue().getRequestMethod(); + String _target = entry.getValue().getActionKey(); + if (target.equals(_target) && !_requestMethod.equals(requestMethod)) { + //请求方法不正确 + throw new ActionException(HttpStatus.METHOD_NOT_ALLOWED.value(), + renderManager.getRenderFactory().getErrorRender(HttpStatus.METHOD_NOT_ALLOWED.value()), + "'" + target + "' is specified as a '" + _requestMethod + "' request. '" + requestMethod + "' requests are not supported"); + } + String[] _paths = entry.getValue().getActionKey().split(SLASH); + if (entry.getValue().getActionKey().startsWith(requestMethod) && + entry.getValue().getActionKey().contains("{") && entry.getValue().getActionKey().contains("}") + && paths.length == _paths.length && RestfulUtils.comparePaths(_paths, paths)) { + restfulAction = entry.getValue(); + break; + } + } + } + return restfulAction; + } +} diff --git a/src/main/java/io/jboot/components/restful/ResponseEntity.java b/src/main/java/io/jboot/components/restful/ResponseEntity.java new file mode 100644 index 00000000..90aad644 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/ResponseEntity.java @@ -0,0 +1,67 @@ +package io.jboot.components.restful; + +import java.util.HashMap; +import java.util.Map; + +public class ResponseEntity { + + //响应的数据 + private T data; + + //自定义响应头部信息 + private Map headers = new HashMap<>(); + + //默认http状态 + private HttpStatus httpStatus = HttpStatus.OK; + + public T getData() { + return data; + } + + public ResponseEntity setData(T data) { + this.data = data; + return this; + } + + public ResponseEntity addHeaders(Map headers) { + this.headers.putAll(headers); + return this; + } + + public ResponseEntity addHeader(String key, String value) { + this.headers.put(key, value); + return this; + } + + public Map getHeaders() { + return headers; + } + + public ResponseEntity setHttpStatus(HttpStatus httpStatus) { + this.httpStatus = httpStatus; + return this; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + public ResponseEntity() { + } + + public ResponseEntity(T data, Map headers, HttpStatus httpStatus) { + this.data = data; + this.headers = headers; + this.httpStatus = httpStatus; + } + + public ResponseEntity(Map headers, HttpStatus httpStatus) { + this.headers = headers; + this.httpStatus = httpStatus; + } + + public ResponseEntity(T data) { + this.data = data; + } + +} diff --git a/src/main/java/io/jboot/components/restful/RestfulAction.java b/src/main/java/io/jboot/components/restful/RestfulAction.java new file mode 100644 index 00000000..1178f8c8 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulAction.java @@ -0,0 +1,24 @@ +package io.jboot.components.restful; + +import com.jfinal.aop.Interceptor; +import com.jfinal.core.Action; +import com.jfinal.core.Controller; + +import java.lang.reflect.Method; + +public class RestfulAction extends Action { + + private String requestMethod; + + public String getRequestMethod() { + return requestMethod; + } + + public RestfulAction(String controllerKey, String actionKey, Class controllerClass, + Method method, String methodName, Interceptor[] interceptors, String viewPath, String requestMethod) { + super(controllerKey, actionKey, controllerClass, method, methodName, interceptors, viewPath); + this.requestMethod = requestMethod; + } + + +} diff --git a/src/main/java/io/jboot/components/restful/RestfulCallback.java b/src/main/java/io/jboot/components/restful/RestfulCallback.java new file mode 100644 index 00000000..00b6bd66 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulCallback.java @@ -0,0 +1,21 @@ +package io.jboot.components.restful; + +import com.jfinal.core.Action; +import com.jfinal.proxy.Callback; + +public class RestfulCallback implements Callback { + + private Action action; + private Object target; + + public RestfulCallback(Action restfulAction, Object target) { + this.action = restfulAction; + this.target = target; + } + + @Override + public Object call(Object[] args) throws Throwable { + return action.getMethod().invoke(target, args); + } + +} diff --git a/src/main/java/io/jboot/components/restful/RestfulHandler.java b/src/main/java/io/jboot/components/restful/RestfulHandler.java new file mode 100644 index 00000000..d6b44370 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulHandler.java @@ -0,0 +1,64 @@ +package io.jboot.components.restful; + +import com.jfinal.aop.Invocation; +import com.jfinal.core.Action; +import com.jfinal.core.Controller; +import com.jfinal.log.Log; +import io.jboot.components.restful.annotation.ResponseHeader; +import io.jboot.components.restful.annotation.ResponseHeaders; +import io.jboot.utils.ArrayUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.handler.JbootActionHandler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class RestfulHandler extends JbootActionHandler { + + private static final Log log = Log.getLog(JbootActionHandler.class); + + @Override + public Action getAction(String target, String[] urlPara, HttpServletRequest request) { + Action action = super.getAction(target, urlPara); + if(action != null){ + if (action.getActionKey().equals("/") && action.getMethodName().equals("index") + && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { + action = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); + } + } + if (action == null) { + action = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); + } + return action; + } + + @Override + public Invocation getInvocation(Action action, Controller controller) { + if (action instanceof RestfulAction) { + Object[] args = RestfulUtils.parseActionMethodParameters(action.getActionKey(), action.getActionKey(), + action.getMethod(), controller.getRequest(), controller.getRawData()); + return new RestfulInvocation(action, controller, args); + } else { + return super.getInvocation(action, controller); + } + } + + @Override + public void setResponse(HttpServletResponse response, Action action) { + ResponseHeader[] responseHeaders = action.getMethod().getAnnotationsByType(ResponseHeader.class); + ResponseHeaders responseHeadersList = action.getMethod().getAnnotation(ResponseHeaders.class); + if (responseHeadersList != null && responseHeadersList.value().length > 0) { + if (responseHeaders != null && responseHeaders.length > 0) { + responseHeaders = ArrayUtil.concat(responseHeaders, responseHeadersList.value()); + } else { + responseHeaders = responseHeadersList.value(); + } + } + if (responseHeaders.length > 0) { + for (ResponseHeader header : responseHeaders) { + response.setHeader(header.key(), header.value()); + } + } + } + +} diff --git a/src/main/java/io/jboot/components/restful/RestfulInvocation.java b/src/main/java/io/jboot/components/restful/RestfulInvocation.java new file mode 100644 index 00000000..99e16b58 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulInvocation.java @@ -0,0 +1,56 @@ +package io.jboot.components.restful; + +import com.jfinal.aop.Invocation; +import com.jfinal.core.Action; +import com.jfinal.core.Controller; + +public class RestfulInvocation extends Invocation { + + private Action action; + + public RestfulInvocation(Action action, Controller controller, Object[] args) { + super(controller, action.getMethod(), action.getInterceptors(), new RestfulCallback(action, controller), args); + this.action = action; + } + + + @Override + public Controller getController() { + return super.getTarget(); + } + + /** + * Return the action key. + * actionKey = controllerKey + methodName + */ + @Override + public String getActionKey() { + return action.getActionKey(); + } + + /** + * Return the controller key. + */ + @Override + public String getControllerKey() { + return action.getControllerKey(); + } + + /** + * Return view path of this controller. + */ + @Override + public String getViewPath() { + return action.getViewPath(); + } + + /** + * return true if it is action invocation. + */ + @Override + public boolean isActionInvocation() { + return action != null; + } + + +} diff --git a/src/main/java/io/jboot/components/restful/RestfulUtils.java b/src/main/java/io/jboot/components/restful/RestfulUtils.java new file mode 100644 index 00000000..e9190698 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulUtils.java @@ -0,0 +1,228 @@ +package io.jboot.components.restful; + + +import com.jfinal.core.ActionException; +import com.jfinal.kit.JsonKit; +import com.jfinal.render.RenderManager; +import io.jboot.components.restful.annotation.*; +import io.jboot.utils.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; + +public class RestfulUtils { + + private static final RenderManager renderManager = RenderManager.me(); + + /** + * 从url中解析路径参数 + * + * @param url + * @param actionKey + * @return + */ + public static Map parsePathVariables(String url, String actionKey) { + if (actionKey.contains("{") && actionKey.contains("}")) { + Map pathVariables = new HashMap<>(); + String[] paths = url.split("/"); + String[] _paths = actionKey.split("/"); + for (int i = 0; i < paths.length; i++) { + if (_paths[i].startsWith("{") && _paths[i].endsWith("}")) { + String pathKey = _paths[i].substring(1, _paths[i].length() - 1); + String value = paths[i]; + pathVariables.put(pathKey, value); + } + } + return pathVariables; + } else { + return null; + } + } + + /** + * 转换请求action请求的参数信息 + * + * @param target + * @param actionKey + * @param actionMethod + * @param request + * @param rawData + * @return + * @throws ActionException + */ + public static Object[] parseActionMethodParameters(String target, String actionKey, Method actionMethod, HttpServletRequest request, String rawData) + throws ActionException { + Object[] args = new Object[actionMethod.getParameters().length]; + for (int i = 0; i < actionMethod.getParameters().length; i++) { + Parameter parameter = actionMethod.getParameters()[i]; + RequestParam requestParam = parameter.getAnnotation(RequestParam.class); + RequestBody requestBody = parameter.getAnnotation(RequestBody.class); + RequestHeader requestHeader = parameter.getAnnotation(RequestHeader.class); + PathVariable pathVariable = parameter.getAnnotation(PathVariable.class); + String parameterName = parameter.getName(); + String values[]; + if (requestParam != null) { + if (StrUtil.isNotBlank(requestParam.value())) { + parameterName = requestParam.value(); + } + values = request.getParameterValues(parameterName); + parameter.getType(); + args[i] = parseRequestParamToParameter(values, parameterName, parameter.getType(), parameter); + if (args[i] == null && requestParam.required()) { + //要求参数不为空,但是却并没有提供参数 + throw genBindError("Parameter '" + parameterName + "' specifies a forced check, but the value is null"); + } + } else if (requestBody != null) { + args[i] = parseRequestBodyToParameter(rawData, parameterName, parameter.getType(), parameter); + } else if (requestHeader != null) { + if (StrUtil.isNotBlank(requestHeader.value())) { + parameterName = requestHeader.value(); + } + String value = request.getHeader(parameterName); + args[i] = parseRequestHeaderToParameter(value, parameterName, parameter.getType(), parameter); + if (args[i] == null && requestHeader.required()) { + //要求参数不为空,但是却并没有提供参数 + throw genBindError("Parameter '" + parameterName + "' specifies a forced check, but the value is null"); + } + } else if (pathVariable != null) { + if (StrUtil.isNotBlank(pathVariable.value())) { + parameterName = pathVariable.value(); + } + args[i] = parsePathVariableToParameter(target, actionKey, parameterName, parameter.getType(), parameter); + } else { + args[i] = null; + } + } + return args; + } + + /** + * 比对url请求路径 + * + * @param sourcePaths action配置的原路径 + * @param targetPaths 请求的目标路径 + * @return + */ + public static boolean comparePaths(String[] sourcePaths, String[] targetPaths) { + int matchingCount = 0; + for (int i = 0; i < sourcePaths.length; i++) { + if (sourcePaths[i].equals(targetPaths[i]) + || (sourcePaths[i].startsWith("{") && sourcePaths[i].endsWith("}"))) { + matchingCount += 1; + } + } + return matchingCount == sourcePaths.length; + } + + private static Object parseRequestParamToParameter(String[] value, String name, Class parameterTypeClass, Parameter parameter) { + if (parameterTypeClass.isArray()) { + Object[] objects = new Object[value.length]; + for (int i = 0; i < value.length; i++) { + objects[i] = parseCommonValue(value[i], name, parameterTypeClass, parameter); + } + return objects; + } else { + if (value != null && value.length > 0) { + return parseCommonValue(value[0], name, parameterTypeClass, parameter); + } + } + + return null; + } + + private static Object parseRequestHeaderToParameter(String header, String name, Class parameterTypeClass, Parameter parameter) { + return parseCommonValue(header, name, parameterTypeClass, parameter); + } + + private static Object parseRequestBodyToParameter(String body, String name, Class parameterTypeClass, Parameter parameter) { + //先当作基本数据来转换 + Object value = parseCommonValue(body, name, parameterTypeClass, parameter); + if (value == null) { + value = JsonKit.parse(body, parameterTypeClass); + } + return value; + } + + private static Object parsePathVariableToParameter(String target, String actionKey, String parameterName, Class parameterTypeClass, Parameter parameter) { + Map pathVariables = parsePathVariables(target, actionKey); + String value = pathVariables.get(parameterName); + return parseCommonValue(value, parameterName, parameterTypeClass, parameter); + } + + /** + * 转换基本类型参数,目前支持string,int,double,float,boolean,long基本类型数据 + * + * @param value + * @param name + * @param parameterTypeClass + * @param parameter + * @return + */ + private static Object parseCommonValue(String value, String name, Class parameterTypeClass, Parameter parameter) { + if (StrUtil.isBlank(value)) { + return null; + } + if (parameterTypeClass.equals(String.class)) { + return value; + } else if (parameterTypeClass.equals(int.class) + || parameterTypeClass.equals(double.class) + || parameterTypeClass.equals(float.class) + || parameterTypeClass.equals(long.class) + || parameterTypeClass.equals(BigDecimal.class) + || parameterTypeClass.equals(short.class)) { + try { + if (parameterTypeClass.equals(int.class)) { + return Integer.valueOf(value); + } else if (parameterTypeClass.equals(double.class)) { + return Double.valueOf(value); + } else if (parameterTypeClass.equals(float.class)) { + return Float.valueOf(value); + } else if (parameterTypeClass.equals(long.class)) { + return Long.valueOf(value); + } else if (parameterTypeClass.equals(BigDecimal.class)) { + return new BigDecimal(value); + } else if (parameterTypeClass.equals(short.class)) { + return Short.valueOf(value); + } else { + return null; + } + } catch (NumberFormatException e) { + throw genBindError("Error resolving parameter '" + name + "', unable to match value '" + + value + "' to specified type '" + parameterTypeClass.getName() + "'"); + } + } else if (parameterTypeClass.equals(boolean.class)) { + return Boolean.valueOf(value); + } else if (parameterTypeClass.equals(Date.class)) { + DateFormat dateFormat = parameter.getAnnotation(DateFormat.class); + SimpleDateFormat simpleDateFormat; + if (dateFormat != null) { + simpleDateFormat = new SimpleDateFormat(dateFormat.value()); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone(dateFormat.timeZone())); + } else { + simpleDateFormat = new SimpleDateFormat(); + } + try { + return simpleDateFormat.parse(value); + } catch (ParseException e) { + throw genBindError("Error resolving parameter '" + name + "', unable to match value '" + + value + "' to specified type '" + parameterTypeClass.getName() + "'"); + } + } else { + return null; + } + } + + private static ActionException genBindError(String message) { + return new ActionException(HttpStatus.BAD_REQUEST.value(), + renderManager.getRenderFactory().getErrorRender(HttpStatus.BAD_REQUEST.value()), message); + } + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java b/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java new file mode 100644 index 00000000..2483f1c3 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java @@ -0,0 +1,19 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 删除delete 方法注解 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface DeleteMapping { + + /** + * url mapping + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/GetMapping.java b/src/main/java/io/jboot/components/restful/annotation/GetMapping.java new file mode 100644 index 00000000..c571a745 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/GetMapping.java @@ -0,0 +1,16 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface GetMapping { + + /** + * url mapping + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/PathVariable.java b/src/main/java/io/jboot/components/restful/annotation/PathVariable.java new file mode 100644 index 00000000..4d0c04c4 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/PathVariable.java @@ -0,0 +1,30 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 路径参数注解 + * /user/{id}/cards + * 支持如下类型参数注入: + * string + * int + * double + * float + * boolean + * long + * bigDecimal + * date + * short + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface PathVariable { + + /** + * 如果为空则默认为参数名本身 + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/PostMapping.java b/src/main/java/io/jboot/components/restful/annotation/PostMapping.java new file mode 100644 index 00000000..e0f9a45a --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/PostMapping.java @@ -0,0 +1,16 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface PostMapping { + + /** + * url mapping + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/PutMapping.java b/src/main/java/io/jboot/components/restful/annotation/PutMapping.java new file mode 100644 index 00000000..8d0c66d2 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/PutMapping.java @@ -0,0 +1,19 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * Put 请求方法定义 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface PutMapping { + + /** + * url mapping + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestBody.java b/src/main/java/io/jboot/components/restful/annotation/RequestBody.java new file mode 100644 index 00000000..ae2d8826 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/RequestBody.java @@ -0,0 +1,23 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 请求体参数注解 + * 支持如下类型参数: + * string + * int + * double + * float + * boolean + * long + * object + * bigDecimal + * date + * short + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface RequestBody { +} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java b/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java new file mode 100644 index 00000000..9a9c361c --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java @@ -0,0 +1,31 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 请求头部信息注解 + * 支持如下类型参数注入: + * string + * int + * double + * float + * boolean + * long + * bigDecimal + * date + * short + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface RequestHeader { + + /** + * 如果为空则默认为参数名本身 + * @return + */ + String value() default ""; + + boolean required() default false; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestParam.java b/src/main/java/io/jboot/components/restful/annotation/RequestParam.java new file mode 100644 index 00000000..906af564 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/RequestParam.java @@ -0,0 +1,31 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 请求参数注解 + * 支持如下类型参数: + * string / string[] + * int / int[] + * double / double[] + * float / float[] + * boolean / boolean[] + * long / long[] + * bigDecimal / bigDecimal[] + * date / date[] + * short / short[] + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface RequestParam { + + /** + * 如果为空则默认为参数名本身 + * @return + */ + String value() default ""; + + boolean required() default false; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java b/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java new file mode 100644 index 00000000..626f22bf --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java @@ -0,0 +1,17 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 自定义响应头 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface ResponseHeader { + + String key(); + + String value(); + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java b/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java new file mode 100644 index 00000000..d51f43ae --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java @@ -0,0 +1,12 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface ResponseHeaders { + + ResponseHeader[] value(); + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/RestController.java b/src/main/java/io/jboot/components/restful/annotation/RestController.java new file mode 100644 index 00000000..6650c476 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/RestController.java @@ -0,0 +1,12 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * rest controller 标识 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface RestController { +} diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 204d9dde..e5b573b9 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -36,6 +36,9 @@ import io.jboot.app.config.support.nacos.NacosConfigManager; import io.jboot.components.gateway.JbootGatewayHandler; import io.jboot.components.gateway.JbootGatewayManager; import io.jboot.components.limiter.LimiterManager; +import io.jboot.components.restful.JbootRestfulManager; +import io.jboot.components.restful.RestfulHandler; +import io.jboot.components.restful.annotation.RestController; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.schedule.JbootScheduleManager; import io.jboot.core.listener.JbootAppListenerManager; @@ -73,7 +76,9 @@ import java.util.Properties; public class JbootCoreConfig extends JFinalConfig { private List routeList = new ArrayList<>(); + private List restfulRoutes = new ArrayList<>(); + private JbootRestfulManager.Config restfulConfig = new JbootRestfulManager.Config(); public JbootCoreConfig() { @@ -173,6 +178,13 @@ public class JbootCoreConfig extends JFinalConfig { continue; } + //检查是否是restful类型的controller,如果是则加入restful专门指定的routes + RestController restController = clazz.getAnnotation(RestController.class); + if (restController != null) { + restfulRoutes.add(new Routes.Route(value, clazz, value)); + continue; + } + String viewPath = AnnotationUtil.get(mapping.viewPath()); if (StrUtil.isNotBlank(viewPath)) { @@ -194,6 +206,19 @@ public class JbootCoreConfig extends JFinalConfig { JbootControllerManager.me().setMapping(route.getControllerKey(), route.getControllerClass()); } + if (!restfulRoutes.isEmpty()) { + //处理restful专属的routes + restfulConfig.setRoutes(restfulRoutes) + .setBaseViewPath(routes.getBaseViewPath()) + .setMappingSupperClass(routes.getMappingSuperClass()) + .setRouteInterceptors(routes.getInterceptors()); + for (Routes.Route route : restfulRoutes) { + JbootControllerManager.me().setMapping(route.getControllerKey(), route.getControllerClass()); + } + routeList.addAll(restfulRoutes); + } + + routeList.addAll(routes.getRouteItemList()); } @@ -271,7 +296,11 @@ public class JbootCoreConfig extends JFinalConfig { //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler if (handlers.getActionHandler() == null) { - handlers.setActionHandler(new JbootActionHandler()); + if (!restfulRoutes.isEmpty()) { + handlers.setActionHandler(new RestfulHandler()); + } else { + handlers.setActionHandler(new JbootActionHandler()); + } } } @@ -294,6 +323,7 @@ public class JbootCoreConfig extends JFinalConfig { JbootSeataManager.me().init(); SentinelManager.me().init(); JbootGatewayManager.me().init(); + JbootRestfulManager.me().init(restfulConfig); JbootAppListenerManager.me().onStart(); } diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index d95d9635..bbb1f6aa 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -18,6 +18,7 @@ package io.jboot.web.render; import com.jfinal.core.Action; import com.jfinal.kit.JsonKit; import com.jfinal.render.*; +import io.jboot.components.restful.ResponseEntity; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -26,6 +27,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Map; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -58,8 +60,17 @@ public class JbootReturnValueRender extends Render { this.render = new TextRender((String) value); } else if (this.value instanceof Date) { this.render = new TextRender(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value)); + } else if(this.value instanceof Render){ + this.render = (Render)this.value; } else { - this.render = new JsonRender(JsonKit.toJson(value)); + if(this.value instanceof ResponseEntity){ + ResponseEntity responseEntity = (ResponseEntity)this.value; + Map headers = responseEntity.getHeaders(); + headers.forEach((k, v) -> this.response.setHeader(k, v)); + this.response.setStatus(responseEntity.getHttpStatus().value()); + this.value = responseEntity.getData(); + } + this.render = new JsonRender(JsonKit.toJson(this.value)); } } diff --git a/src/test/java/io/jboot/test/restful/RestfulController.java b/src/test/java/io/jboot/test/restful/RestfulController.java new file mode 100644 index 00000000..a3aa8932 --- /dev/null +++ b/src/test/java/io/jboot/test/restful/RestfulController.java @@ -0,0 +1,138 @@ +package io.jboot.test.restful; + +import com.jfinal.aop.Before; +import com.jfinal.aop.Inject; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import com.jfinal.core.NotAction; +import com.jfinal.kit.JsonKit; +import com.jfinal.kit.StrKit; +import io.jboot.components.restful.HttpStatus; +import io.jboot.components.restful.ResponseEntity; +import io.jboot.components.restful.annotation.*; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; +import io.jboot.web.cors.EnableCORS; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@RestController +@RequestMapping("/restful") +public class RestfulController extends JbootController { + + public static class Data implements Serializable { + private String id; + private String name; + private int age; + + public Data(String id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + } + + public static class RestfulInterceptor implements Interceptor { + + public void intercept(Invocation inv) { + System.out.println("--------> restful request begin"); + inv.invoke(); + System.out.println("--------> restful request end"); + } + } + + @Inject + private RestfulService restfulService; + + @NotAction + public List initData(){ + List users = new ArrayList<>(); + users.add(new RestfulController.Data("1", "tom", 18)); + users.add(new RestfulController.Data("2", "andy", 29)); + users.add(new RestfulController.Data("3", "max", 13)); + return users; + } + + //GET /restful + @GetMapping + @EnableCORS + @Before({RestfulInterceptor.class}) + @ResponseHeaders({@ResponseHeader(key = "d-head-1", value = "a"), @ResponseHeader(key = "d-head-2", value = "b")}) + public List users(){ + return initData(); + } + + // GET /restful/randomKey + @GetMapping("/randomKey") + public String randomKey(){ + return restfulService.getRandomKey(); + } + + // GET /restful/users + @GetMapping("/users") + public ResponseEntity> entityUsers(){ + return new ResponseEntity<>(initData()).addHeader("x-token", StrKit.getRandomUUID()).setHttpStatus(HttpStatus.ACCEPTED); + } + + // PUT /restful + @PutMapping + public void create(@RequestBody RestfulController.Data data){ + System.out.println("get request body data:\n" + JsonKit.toJson(data)); + } + + // PUT /restful/createList + @PutMapping("/createList") + public void createUsers(@RequestBody List users){ + System.out.println("get request body data:\n" + JsonKit.toJson(users)); + } + + // DELETE /restful/:id + @DeleteMapping("/{id}") + public void delete(@PathVariable("id") String id){ + System.out.println("delete by id : " + id ); + } + + // DELETE /restful/delete + @DeleteMapping("/delete") + public void deleteByName(@RequestParam(value = "name", required = true) String name, + @RequestHeader String token){ + System.out.println("delete by name : " + name); + System.out.println("get token header : " + token); + } + + @GetMapping("/get-date") + public Date getDate(){ + return new Date(); + } + + @PutMapping("/input-date") + public void inputDate(@RequestParam @DateFormat Date date){ + System.out.println("input date:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); + renderNull(); + } + + @PutMapping("/input-big-decimal") + public void inputBigDecimal(@RequestParam BigDecimal num){ + System.out.println("input bigDecimal:"+num.toString()); + renderNull(); + } + + +} diff --git a/src/test/java/io/jboot/test/restful/RestfulService.java b/src/test/java/io/jboot/test/restful/RestfulService.java new file mode 100644 index 00000000..aea7b1cd --- /dev/null +++ b/src/test/java/io/jboot/test/restful/RestfulService.java @@ -0,0 +1,5 @@ +package io.jboot.test.restful; + +public interface RestfulService { + String getRandomKey(); +} diff --git a/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java b/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java new file mode 100644 index 00000000..1c042af3 --- /dev/null +++ b/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java @@ -0,0 +1,12 @@ +package io.jboot.test.restful; + +import com.jfinal.kit.StrKit; +import io.jboot.aop.annotation.Bean; + +@Bean +public class RestfulServiceImpl implements RestfulService { + @Override + public String getRandomKey() { + return StrKit.getRandomUUID(); + } +} -- Gitee From 2494693d2666b063bf19a39f6de5d6f7a9425c4d Mon Sep 17 00:00:00 2001 From: yangyao Date: Mon, 6 Apr 2020 18:03:48 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0responseEntity=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/restful/ResponseEntity.java | 18 +++++++-------- .../web/render/JbootReturnValueRender.java | 23 +++++++++++-------- .../jboot/test/restful/RestfulController.java | 6 +++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/jboot/components/restful/ResponseEntity.java b/src/main/java/io/jboot/components/restful/ResponseEntity.java index 90aad644..bae89f31 100644 --- a/src/main/java/io/jboot/components/restful/ResponseEntity.java +++ b/src/main/java/io/jboot/components/restful/ResponseEntity.java @@ -3,10 +3,10 @@ package io.jboot.components.restful; import java.util.HashMap; import java.util.Map; -public class ResponseEntity { +public class ResponseEntity { //响应的数据 - private T data; + private Object data; //自定义响应头部信息 private Map headers = new HashMap<>(); @@ -14,21 +14,21 @@ public class ResponseEntity { //默认http状态 private HttpStatus httpStatus = HttpStatus.OK; - public T getData() { + public Object getData() { return data; } - public ResponseEntity setData(T data) { + public ResponseEntity setData(Object data) { this.data = data; return this; } - public ResponseEntity addHeaders(Map headers) { + public ResponseEntity addHeaders(Map headers) { this.headers.putAll(headers); return this; } - public ResponseEntity addHeader(String key, String value) { + public ResponseEntity addHeader(String key, String value) { this.headers.put(key, value); return this; } @@ -37,7 +37,7 @@ public class ResponseEntity { return headers; } - public ResponseEntity setHttpStatus(HttpStatus httpStatus) { + public ResponseEntity setHttpStatus(HttpStatus httpStatus) { this.httpStatus = httpStatus; return this; } @@ -49,7 +49,7 @@ public class ResponseEntity { public ResponseEntity() { } - public ResponseEntity(T data, Map headers, HttpStatus httpStatus) { + public ResponseEntity(Object data, Map headers, HttpStatus httpStatus) { this.data = data; this.headers = headers; this.httpStatus = httpStatus; @@ -60,7 +60,7 @@ public class ResponseEntity { this.httpStatus = httpStatus; } - public ResponseEntity(T data) { + public ResponseEntity(Object data) { this.data = data; } diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index bbb1f6aa..6061a7ff 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -60,17 +60,14 @@ public class JbootReturnValueRender extends Render { this.render = new TextRender((String) value); } else if (this.value instanceof Date) { this.render = new TextRender(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value)); - } else if(this.value instanceof Render){ - this.render = (Render)this.value; + } else if (this.value instanceof Render) { + this.render = (Render) this.value; } else { - if(this.value instanceof ResponseEntity){ - ResponseEntity responseEntity = (ResponseEntity)this.value; - Map headers = responseEntity.getHeaders(); - headers.forEach((k, v) -> this.response.setHeader(k, v)); - this.response.setStatus(responseEntity.getHttpStatus().value()); - this.value = responseEntity.getData(); + if (this.value instanceof ResponseEntity) { + this.render = new JsonRender(((ResponseEntity) this.value).getData()); + } else { + this.render = new JsonRender(JsonKit.toJson(this.value)); } - this.render = new JsonRender(JsonKit.toJson(this.value)); } } @@ -78,17 +75,25 @@ public class JbootReturnValueRender extends Render { @Override public Render setContext(HttpServletRequest request, HttpServletResponse response) { render.setContext(request, response); + super.setContext(request, response); return this; } @Override public Render setContext(HttpServletRequest request, HttpServletResponse response, String viewPath) { render.setContext(request, response, viewPath); + super.setContext(request, response, viewPath); return this; } @Override public void render() { + if (this.value instanceof ResponseEntity) { + ResponseEntity responseEntity = (ResponseEntity) this.value; + Map headers = responseEntity.getHeaders(); + headers.forEach((k, v) -> response.setHeader(k, v)); + response.setStatus(responseEntity.getHttpStatus().value()); + } this.render.render(); } diff --git a/src/test/java/io/jboot/test/restful/RestfulController.java b/src/test/java/io/jboot/test/restful/RestfulController.java index a3aa8932..ab1fc96c 100644 --- a/src/test/java/io/jboot/test/restful/RestfulController.java +++ b/src/test/java/io/jboot/test/restful/RestfulController.java @@ -87,8 +87,10 @@ public class RestfulController extends JbootController { // GET /restful/users @GetMapping("/users") - public ResponseEntity> entityUsers(){ - return new ResponseEntity<>(initData()).addHeader("x-token", StrKit.getRandomUUID()).setHttpStatus(HttpStatus.ACCEPTED); + public ResponseEntity entityUsers(){ + return new ResponseEntity(initData()) + .addHeader("x-token", StrKit.getRandomUUID()) + .setHttpStatus(HttpStatus.ACCEPTED); } // PUT /restful -- Gitee