diff --git a/src/org/redkale/net/http/HttpRequest.java b/src/org/redkale/net/http/HttpRequest.java index fb26631e1..6d32d73be 100644 --- a/src/org/redkale/net/http/HttpRequest.java +++ b/src/org/redkale/net/http/HttpRequest.java @@ -304,6 +304,35 @@ public class HttpRequest extends Request { return array.toString(UTF8); } + /** + * 获取请求内容的JavaBean对象 + * + * @param 泛型 + * @param type 类型 + * + * @return 内容 + */ + public T getBodyJson(java.lang.reflect.Type type) { + String str = array.toString(UTF8); + if (str == null || str.isEmpty()) return null; + return context.getJsonConvert().convertFrom(type, str); + } + + /** + * 获取请求内容的JavaBean对象 + * + * @param 泛型 + * @param convert JsonConvert + * @param type 类型 + * + * @return 内容 + */ + public T getBodyJson(JsonConvert convert, java.lang.reflect.Type type) { + String str = array.toString(UTF8); + if (str == null || str.isEmpty()) return null; + return convert.convertFrom(type, str); + } + /** * 获取请求内容的byte[] * diff --git a/src/org/redkale/net/http/Rest.java b/src/org/redkale/net/http/Rest.java index a6a240c3b..bba3706c6 100644 --- a/src/org/redkale/net/http/Rest.java +++ b/src/org/redkale/net/http/Rest.java @@ -115,6 +115,7 @@ public final class Rest { final String webServletDesc = Type.getDescriptor(WebServlet.class); final String reqDesc = Type.getDescriptor(HttpRequest.class); final String respDesc = Type.getDescriptor(HttpResponse.class); + final String contextDesc = Type.getDescriptor(HttpContext.class); final String retDesc = Type.getDescriptor(RetResult.class); final String futureDesc = Type.getDescriptor(CompletableFuture.class); final String flipperDesc = Type.getDescriptor(Flipper.class); @@ -129,6 +130,7 @@ public final class Rest { final String reqInternalName = Type.getInternalName(HttpRequest.class); final String respInternalName = Type.getInternalName(HttpResponse.class); + final String contextInternalName = Type.getInternalName(HttpContext.class); final String attrInternalName = Type.getInternalName(org.redkale.util.Attribute.class); final String retInternalName = Type.getInternalName(RetResult.class); final String serviceTypeInternalName = Type.getInternalName(serviceType); @@ -256,6 +258,8 @@ public final class Rest { //将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法 final Map> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType); + final Map bodyTypes = new HashMap<>(); + for (final MappingEntry entry : entrys) { final Method method = entry.mappingMethod; final Class returnType = method.getReturnType(); @@ -350,7 +354,7 @@ public final class Rest { if (anncookie != null) throw new RuntimeException("@RestBody and @RestCookie cannot on the same Parameter in " + method); if (annsid != null) throw new RuntimeException("@RestBody and @RestSessionid cannot on the same Parameter in " + method); if (annaddr != null) throw new RuntimeException("@RestBody and @RestAddress cannot on the same Parameter in " + method); - if (ptype != String.class && ptype != byte[].class) throw new RuntimeException("@RestBody must on String or byte[] Parameter in " + method); + if (ptype.isPrimitive()) throw new RuntimeException("@RestBody cannot on primitive type Parameter in " + method); } RestParam annpara = param.getAnnotation(RestParam.class); @@ -373,7 +377,7 @@ public final class Rest { && (entry.name.startsWith("find") || entry.name.startsWith("delete")) && params.length == 1) { if (ptype.isPrimitive() || ptype == String.class) n = "#"; } - paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody}); + paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, param.getParameterizedType()}); } Map mappingMap = new LinkedHashMap<>(); @@ -415,7 +419,7 @@ public final class Rest { av0 = mv.visitAnnotation(webparamsDesc, true); AnnotationVisitor av1 = av0.visitArray("value"); //设置 WebParam - for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody} + for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, pgentype} final boolean ishead = ((RestHeader) ps[9]) != null; //是否取getHeader 而不是 getParameter final boolean iscookie = ((RestCookie) ps[10]) != null; //是否取getCookie @@ -449,6 +453,7 @@ public final class Rest { RestHeader annhead = (RestHeader) ps[9]; RestCookie anncookie = (RestCookie) ps[10]; RestBody annbody = (RestBody) ps[11]; + java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[12]; final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter final boolean iscookie = anncookie != null; //是否取getCookie @@ -488,11 +493,21 @@ public final class Rest { mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyUTF8", "()Ljava/lang/String;", false); mv.visitVarInsn(ASTORE, maxLocals); varInsns.add(new int[]{ALOAD, maxLocals}); - } else { + } else if (ptype == byte[].class) { mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBody", "()[B", false); mv.visitVarInsn(ASTORE, maxLocals); varInsns.add(new int[]{ALOAD, maxLocals}); + } else { //JavaBean 转 Json + String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); + bodyTypes.put(typefieldname, pgentype); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype)); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); } } else if ("#".equals(pname)) { //从request.getRequstURI 中取参数 if (ptype == boolean.class) { @@ -771,12 +786,12 @@ public final class Rest { RestSessionid rs = field.getAnnotation(RestSessionid.class); RestAddress ra = field.getAnnotation(RestAddress.class); RestBody rb = field.getAnnotation(RestBody.class); - if (rh == null && rc == null && ra == null) continue; + if (rh == null && rc == null && ra == null && rb == null) continue; if (rh != null && field.getType() != String.class) throw new RuntimeException("@RestHeader must on String Field in " + field); if (rc != null && field.getType() != String.class) throw new RuntimeException("@RestCookie must on String Field in " + field); if (rs != null && field.getType() != String.class) throw new RuntimeException("@RestSessionid must on String Field in " + field); if (ra != null && field.getType() != String.class) throw new RuntimeException("@RestAddress must on String Field in " + field); - if (rb != null && field.getType() != String.class && field.getType() != byte[].class) throw new RuntimeException("@RestAddress must on String or byte[] Field in " + field); + if (rb != null && field.getType().isPrimitive()) throw new RuntimeException("@RestBody must on cannot on primitive type Field in " + field); org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field); String attrFieldName; String restname = ""; @@ -798,11 +813,14 @@ public final class Rest { } else if (rb != null && field.getType() == byte[].class) { attrFieldName = "_redkale_attr_bodybytes_" + restAttributes.size(); //restname = ""; + } else if (rb != null && field.getType() != String.class && field.getType() != byte[].class) { + attrFieldName = "_redkale_attr_bodyjson_" + restAttributes.size(); + //restname = ""; } else { continue; } restAttributes.put(attrFieldName, attr); - attrParaNames.put(attrFieldName, new Object[]{restname, field.getType()}); + attrParaNames.put(attrFieldName, new Object[]{restname, field.getType(), field.getGenericType()}); fields.add(field.getName()); } } while ((loop = loop.getSuperclass()) != Object.class); @@ -833,6 +851,13 @@ public final class Rest { mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyUTF8", "()Ljava/lang/String;", false); } else if (en.getKey().contains("_bodybytes_")) { mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBody", "()[B", false); + } else if (en.getKey().contains("_bodyjson_")) {//JavaBean 转 Json + String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); + bodyTypes.put(typefieldname, (java.lang.reflect.Type) en.getValue()[2]); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, Type.getInternalName((Class) en.getValue()[1])); } mv.visitMethodInsn(INVOKEINTERFACE, attrInternalName, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", true); } @@ -978,6 +1003,11 @@ public final class Rest { mappingMaps.add(mappingMap); } // end for each + for (Map.Entry en : bodyTypes.entrySet()) { + fv = cw.visitField(ACC_PRIVATE, en.getKey(), "Ljava/lang/reflect/Type;", null, null); + fv.visitEnd(); + } + for (String attrname : restAttributes.keySet()) { fv = cw.visitField(ACC_PRIVATE, attrname, attrDesc, null, null); fv.visitEnd(); @@ -1008,6 +1038,11 @@ public final class Rest { attrField.setAccessible(true); attrField.set(obj, en.getValue()); } + for (Map.Entry en : bodyTypes.entrySet()) { + Field genField = newClazz.getDeclaredField(en.getKey()); + genField.setAccessible(true); + genField.set(obj, en.getValue()); + } Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME); typesfield.setAccessible(true); java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramtypes.size()][]; diff --git a/test/org/redkale/test/rest/HelloAsyncHandler.java b/test/org/redkale/test/rest/HelloAsyncHandler.java new file mode 100644 index 000000000..64c5b79d0 --- /dev/null +++ b/test/org/redkale/test/rest/HelloAsyncHandler.java @@ -0,0 +1,26 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.rest; + +import org.redkale.util.AsyncHandler; + +/** + * + * @author zhangjx + */ +public class HelloAsyncHandler implements AsyncHandler { + + @Override + public void completed(Object result, Object attachment) { + System.out.println("-----HelloAsyncHandler--------result : " + result + ", attachment: " + attachment); + } + + @Override + public void failed(Throwable exc, Object attachment) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/test/org/redkale/test/rest/HelloEntity.java b/test/org/redkale/test/rest/HelloEntity.java index 1f0b738fd..f2dd74022 100644 --- a/test/org/redkale/test/rest/HelloEntity.java +++ b/test/org/redkale/test/rest/HelloEntity.java @@ -1,5 +1,6 @@ package org.redkale.test.rest; +import java.util.Map; import javax.persistence.Id; import org.redkale.convert.json.JsonFactory; import org.redkale.net.http.*; @@ -28,9 +29,19 @@ public class HelloEntity { @RestBody private byte[] bodys; + @RestBody + private Map bodymap; + @RestAddress private String clientaddr; + public HelloEntity() { + } + + public HelloEntity(int id) { + this.helloid = id; + } + /** 以下省略getter setter方法 */ public int getHelloid() { return helloid; @@ -104,6 +115,14 @@ public class HelloEntity { this.bodys = bodys; } + public Map getBodymap() { + return bodymap; + } + + public void setBodymap(Map bodymap) { + this.bodymap = bodymap; + } + @Override public String toString() { return JsonFactory.root().getConvert().convertTo(this); diff --git a/test/org/redkale/test/rest/HelloService.java b/test/org/redkale/test/rest/HelloService.java index fac6b6cf8..5e7e9f646 100644 --- a/test/org/redkale/test/rest/HelloService.java +++ b/test/org/redkale/test/rest/HelloService.java @@ -34,10 +34,11 @@ public class HelloService implements Service { } //增加记录 - public RetResult createHello(UserInfo info, HelloEntity entity) { + public RetResult createHello(UserInfo info, HelloEntity entity, @RestBody Map body) { + System.out.println("增加记录----------------" + nodeid + ": body =" + body + ", entity =" + entity); entity.setCreator(info == null ? 0 : info.getUserid()); //设置当前用户ID entity.setCreatetime(System.currentTimeMillis()); - source.insert(entity); + if (source != null) source.insert(entity); return new RetResult<>(entity); } @@ -88,7 +89,24 @@ public class HelloService implements Service { //异步查询单个 @RestMapping(name = "asyncfind") public CompletableFuture asyncFindHello(@RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 - if (source != null) source.findAsync(HelloEntity.class, id); + if (source != null) source.findAsync(HelloEntity.class, id); + System.out.println("------------进入asyncfind1-------"); return CompletableFuture.completedFuture(new HelloEntity()); } + + //异步查询单个 + @RestMapping(name = "asyncfind2") + public void asyncFindHello(AsyncHandler hander, @RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 + if (source != null) source.findAsync(HelloEntity.class, id); + System.out.println("-----------进入asyncfind2--------" + hander); + hander.completed(new HelloEntity(id), id); + } + + //异步查询单个 + @RestMapping(name = "asyncfind3") + public void asyncFindHello(HelloAsyncHandler hander, @RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 + if (source != null) source.findAsync(HelloEntity.class, id); + System.out.println("-----------进入asyncfind3--------" + hander); + hander.completed(new HelloEntity(id), id); + } } diff --git a/test/org/redkale/test/rest/_DynHelloRestServlet1.java b/test/org/redkale/test/rest/_DynHelloRestServlet1.java index 64bce0019..04a3fb701 100644 --- a/test/org/redkale/test/rest/_DynHelloRestServlet1.java +++ b/test/org/redkale/test/rest/_DynHelloRestServlet1.java @@ -45,6 +45,16 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { url = "http://127.0.0.1:" + port + "/pipes/hello/listmap?map={'a':5}"; System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); + url = "http://127.0.0.1:" + port + "/pipes/hello/create?entity={}"; + System.out.println("增加记录: " + Utility.postHttpContent(url, headers, "{'a':2,'b':3}")); + + url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind/111111"; + System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); + url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind2/22222"; + System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); + url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind3/333333"; + System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); + } @AuthIgnore @@ -55,7 +65,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { bean.setClientaddr(req.getRemoteAddr()); bean.setResname(req.getHeader("hello-res")); UserInfo user = currentUser(req); - RetResult result = service.createHello(user, bean); + RetResult result = service.createHello(user, bean, req.getBodyJson(Map.class)); resp.finishJson(result); } @@ -135,4 +145,20 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { int id = Integer.parseInt(req.getRequstURILastPath()); resp.finishJson(service.asyncFindHello(id)); } + + @AuthIgnore + @HttpMapping(url = "/hello/asyncfind2/") + public void asyncfind2(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + service.asyncFindHello(resp.createAsyncHandler(), id); + } + + @AuthIgnore + @HttpMapping(url = "/hello/asyncfind3/") + public void asyncfind3(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + service.asyncFindHello(resp.createAsyncHandler(HelloAsyncHandler.class), id); + } }