美文网首页
造一个方形的轮子7--Controller支持(下)

造一个方形的轮子7--Controller支持(下)

作者: 爱笑笑_ixx | 来源:发表于2019-07-17 14:33 被阅读0次

    造一个方形轮子文章目录:造一个方形的轮子

    01、添加DispatcherServlet

    接上一篇《造一个方形的轮子6--Controller支持(上)》
    接下来添加处理HTTP请求最核心的类DispatcherServlet,JAVA里提供的最原始的支持WEB的标准就是Servlet规范,类似Tomcat、Jetty都实现了Servlet规范,所以添加一个DispatcherServlet类配置到Tomcat容器中,接管所以路径的请求,在统一处理,就可以实现我们的目的,整理一下DispatcherServlet类的处理流程:

    1、获取HTTP请求类型、ContextPath及RequestURI

    2、使用HTTP请求类型:请求路径到Beans容器中获取对应的ControllerObject对象

    3、如果没有对应的方法映射则返回404

    4、有对应的方法,则根据参数列表从request中获取对应的参数

    5、使用反射方法执行对应的方法并获取返回结果

    6、如果返回结果是String类型直接返回,其它类型使用JSON序列化后输出

    以下是DispatcherServlet.java代码:

    package com.jisuye.core;
    // import ...
    /**
     * 统一请求Servlet处理
     * @author ixx
     * @date 2019-07-14
     */
    public class DispatcherServlet extends HttpServlet {
        private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class);
        private BeansMap beansMap = new BeansMap();
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 解析url
            String contextPath = req.getContextPath();
            String httpMethod = req.getMethod();
            String uri = req.getRequestURI();
            // 匹配到对应的controller
            String controllerKey = httpMethod.toLowerCase()+":"+uri.replace(contextPath, "");
            ControllerObject controllerObject = beansMap.getController(controllerKey);
            // 如果没有匹配,返回404
            if(controllerObject == null){
                resp.sendError(404);
            } else {
                // 执行对应方法
                Object obj = controllerObject.invoke(req);
                // 处理返回结果
                String json;
                if (obj instanceof String) {
                    json = (String) obj;
                } else {
                    json = JSON.toJSONString(obj);
                    resp.setHeader("content-type", "application/json;charset=UTF-8");
                }
                log.info("http request path:" + controllerKey);
                log.info("exec method :" + controllerObject.getMethod().getName());
                log.info("response:" + json);
                resp.getWriter().print(json);
            }
        }
    }
    

    对应的修改ControllerObject类,添加如下方法:

    /**
     * controller对象
     * @author ixx
     * @date 2019-07-14
     */
    public class ControllerObject {
        ......
        /**
         * 反射执行controller方法
         * @param req
         * @return
         */
        public Object invoke(HttpServletRequest req){
            Object[] os = new Object[params.length];
            int i = 0;
            for (SquareParam param : params) {
                // 如果是String类型则当然参数名从req中取值 否则 当做类 去反射生成
                if(param.isParam()){
                    os[i++] = toBasicDataType(req.getParameter(param.getParamName()), param.getClazz());
                } else {
                    String body = getBody(req);
                    Object tmp = JSON.toJavaObject(JSONObject.parseObject(body), param.getClazz());
                    os[i++] = tmp;
                }
            }
            try {
                Object o = this.getMethod().invoke(this.getObject(), os);
                return o;
            } catch (Exception e) {
                log.error("Controller method.invoke() is error!", e);
                throw new SquareException("Controller method.invoke() is error!", e);
            }
        }
    
        private Object toBasicDataType(Object obj, Class clazz){
            if(obj == null || clazz == null){
                return obj;
            }
            switch (clazz.getName()){
                case "int":
                case "java.lang.Integer" : obj = Integer.parseInt(obj.toString()); break;
                case "long":
                case "java.lang.Long" : obj = Long.parseLong(obj.toString()); break;
                case "double":
                case "java.lang.Double" : obj = Double.parseDouble(obj.toString()); break;
                case "float":
                case "java.lang.Float" : obj = Float.parseFloat(obj.toString()); break;
                case "boolean":
                case "java.lang.Boolean" : obj = Boolean.parseBoolean(obj.toString()); break;
                case "char":
                case "java.lang.Character" : obj = obj.toString().charAt(0); break;
                case "byte":
                case "java.lang.Byte" : obj = Byte.parseByte(obj.toString()); break;
                default: break;
            }
            return obj;
        }
        
        public String getBody(HttpServletRequest req){
            String body = "";
            try {
                BufferedReader br = req.getReader();
                String tmp;
                while ((tmp = br.readLine()) != null){
                    body += tmp;
                }
            } catch (IOException e) {
                log.error("getBody data error!", e);
                throw new SquareException("Controller parameter getBody data error!", e);
            }
            return body;
        }
        ......
    }
    

    02、配置DispatcherServlet

    到目前为止基础的代码基本写完了,现在把DispatcherServlet配置到Tomcat中,修改SquareApplication.run方法:

    public class SquareApplication {
        // ......
        public static void run(Class clzz, String[] args) {
           ......
                tomcat = new Tomcat();
                // 设置Tomcat工作目录
                tomcat.setBaseDir(classesPathUtil.getProjectPath() + "/Tomcat");
                tomcat.setPort(TOMCAT_PORT);
                Context context = tomcat.addWebapp(CONTEXT_PATH, classesPathUtil.getPublicPath());
                // 添加DsipatcherServlet
                Wrapper wrapper = Tomcat.addServlet(context, "DispatcherServlet", new DispatcherServlet());
                wrapper.addMapping("/");
                ......
                tomcat.start();
                ......
        }
    }
    

    03、添加测试类

    现在添加测试类,测试一下Controller。

    这里添加一个TestController,有三个方法分别测试Get、Post、Delete方法(Put方法跟Post相同就不做单独测试),参数覆盖@RequestParam和@RequestBody 两种,再结合JdbcTemplate测试一下数据库操作。

    TestController.java:

    package com.jisuye.service;
    // import ...
    @Controller("/test")
    public class TestController {
        @Resource
        private JdbcTemplate jdbcTemplate;
        @GetMapping("/hello")
        public List<AbcEntity> test(@RequestParam("name") String name, @RequestParam("a") String age){
            List<AbcEntity> list = jdbcTemplate.select("select * from abc where name=?", AbcEntity.class, name);
            return list;
        }
    
        @DeleteMapping
        public String testDel(@RequestParam("id") int id){
            int i = jdbcTemplate.delete("delete from abc where id=?", id);
            return "delete id : "+id+" is success";
        }
    
        @PostMapping("/post")
        public ResponseVo testPost(@RequestParam("id") int id, @RequestBody TestVo vo){
            ResponseVo responseVo = new ResponseVo();
            responseVo.setResId(id*10);
            responseVo.setResAge(vo.getAge()*2);
            responseVo.setResName(vo.getName());
            return responseVo;
        }
    }
    
    

    TestVo.java:

    package com.jisuye.service;
    public class TestVo {
        private String name;
        private int age;
        // getter and setter ...
    }
    

    ResponseVo.java:

    package com.jisuye.service;
    public class ResponseVo {
        private int resId;
        private String resName;
        private int resAge;
        // getter and setter ...
    }
    
    

    04、测试结果

    启动程序,使用Postman测试。

    测试Get请求

    GET http://localhost:8888/abc/test/hello?name=ixx&a=23

    响应结果:

    [
        {
            "id": 2,
            "name": "ixx"
        },
        {
            "id": 3,
            "name": "ixx"
        },
        {
            "id": 4,
            "name": "ixx"
        }
    ]
    

    测试Post请求

    POST http://localhost:8888/abc/test/post?id=3

    body:

    {
        "name":"ixx",
        "age":18
    }
    

    响应结果:

    {
        "resAge": 36,
        "resId": 30,
        "resName": "ixx"
    }
    

    测试Delete请求

    DELETE http://localhost:8888/abc/test?id=2

    响应结果:

    delete id : 2 is success
    

    测试404请求

    测试一下如果路径不存在的情况:

    GET http://localhost:8888/abc/test/hello4

    响应结果:

    <!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} a {color:black;} a.name {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/9.0.17</h3></body></html>
    

    可以看到,返回了404的错误页面。

    05、遗留问题

    Controller最最基本的功能实现了,但还有很多问题没有处理,比如程序异常的处理(返回500)、form表单参数、文件上传,参数默认值等,下一篇有可能挑一部分解决一下吧...

    本篇代码地址: https://github.com/iuv/square/tree/square6

    本文作者: ixx
    本文链接: http://jianpage.com/2019/07/17/square7
    版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!

    相关文章

      网友评论

          本文标题:造一个方形的轮子7--Controller支持(下)

          本文链接:https://www.haomeiwen.com/subject/tuvqlctx.html