美文网首页IT@程序员猿媛码农的世界互联网科技
Kotlin x Nodejs 封装方案,愉快的玩转服务端开发吧

Kotlin x Nodejs 封装方案,愉快的玩转服务端开发吧

作者: 何晓杰Dev | 来源:发表于2019-04-24 16:52 被阅读32次

    前几天写了《Kotlin x Nodejs》的具体操作方法,然而这些操作方法却并不简单,甚至有不少地方是完全没有代码提示的(取决于 dynamic 的特性,无法提示),这会对开发造成很大的困扰。

    因此我们就必须有办法来对相应的 js 库进行包装,把相关的 api 暴露出来。按下面的这段代码举例:

    app.post("/upload", upload.single("file")) { req, resp ->
        println(req.file.path)
        resp.end()
    }
    

    其中 req.file.path 怎么来的?说白了很简单,无非是 req 对象里有一个 file 对象,而 file 对象里又有一个名为 path 的属性,问题是我们怎么知道有这些。在标准的 Nodejs 开发中,我们可以看到相关模块的源码,而在 Kotlin 中却无法很直观的去看了。

    这个时候我们有两种做法,一种当然是去 Nodejs 模块的源码里翻了,还有一种比较简单:

    app.post("/upload", upload.single("file")) { req, resp ->
        js("for (var prop in req.file) { console.log(prop); }")
        resp.end()
    }
    

    然后就可以在终端看到打印出来的内容:

    fieldname
    originalname
    encoding
    mimetype
    destination
    filename
    path
    size
    

    这就是对象里面包含的字段了,如果对字段里的具体内容感兴趣,可以改造一下代码:

    js("for (var prop in req.file) { console.log(prop + ' = ' + req.file[prop]); }")
    

    这样就可以把具体内容也打印出来,方便我们判断数据类型。这里可能会有人问,js() 命令执行一句 js 代码,而 req.file 其实是在这段代码外的,为什么可以执行到呢,对象是如何传进去的?其实在这边,就是一个典型的 “想多了” 的场景。因为 Kotlin Javascript 在编译后,就是一个 js,生成的代码如下:

    function App$get$lambda(closure$callback) {
        return function (req, resp) {
            for (var prop in req.file) {
                  console.log(prop + ' = ' + req.file[prop]);
            }
            closure$callback(new Request(req), new Response(resp));
            return Unit;
        };
    }
    

    所以在这里,是完全不会有上下文的问题的,对象可以传入的原理就这么简单。

    然后的事情就会比较有意思了,对 req.file 做一个简单的封装:

    class File(private val base: dynamic) {
        val fieldname: String get() = base.fieldname
        val originalname: String get() = base.originalname
        val encoding: String get() = base.encoding
        val mimetype: String get() = base.mimetype
        val destination: String get() = base.destination
        val filename: String get() = base.filename
        val path: String get() = base.path
        val size: Long get() = base.size
        companion object
    }
    

    然后我们就可以在 app.post() 里这么用:

    app.post("/upload", upload.single("file")) { req, resp ->
        val file = File(req.file)
        println(file.path)
        resp.end()
    }
    

    由于有了这么一个类的包装,Idea 终于可以提示代码了,这意味着会使开发变得更加的方便。


    所以,在这些的基础上,我们可以轻松的写出各种包装器,把 Nodejs 相关的库进一步 Kotlin 化。例如:

    class App(val base: dynamic) {
        fun use(c: dynamic) = base.use(c)
        fun set(key: String, value: dynamic) = base.set(key, value)
        fun listen(port: Int = 8888, callback: () -> Unit) = base.listen(port) { callback() }
        fun get(path: String, callback: (req: Request, resp: Response) -> Unit) =
            base.get(path) { req, resp -> callback(Request(req), Response(resp)) }
        fun post(path: String, callback: (req: Request, resp: Response) -> Unit) =
            base.post(path) { req, resp -> callback(Request(req), Response(resp)) }
        fun post(path: String, option: dynamic, callback: (req: Request, resp: Response) -> Unit) =
            base.post(path, option) { req, resp -> callback(Request(req), Response(resp)) }
    }
    

    好了,说了这么多,也是时候来点真材实料的东西了,其实我已经封装好了一个最简单的 Nodejs + Express 的包装器,在开发的时候简单的使用就好了,在这包装器的基础上,编写 Kotlin 代码是非常轻松的,如下:

    fun main(args: Array<String>) {
        errorHandler = object : ErrorHandler() {  // 全局的异常处理
            override fun handleRejection(err: String?, promise: dynamic) { }
            override fun handleException(err: String?) { }
        } 
        initServer()  // 初始化服务器
        mongoConnect("sampledb") { succ -> }   // 连接到 mongoldb
    
        routing("/index") { req, resp ->  // 路由处理
            if (req.session.views) {   // session 处理
                req.session.views += 1
            } else {
                req.session.views = 1
            }
            println("view => ${req.session.views}")
            val name = req.query.name  // 参数处理
            resp.type("text/html")
            val html = renderFile("index.html", optionOf("username" to name))    // 页面模板渲染
            resp.send(html)
        }
    
        routing("/sql") { _, resp ->  // 查询 mongodb
            mongo?.select("user", listOf("name", "age"), optionOf()) { succ, result ->
                if (succ) {
                    result.forEach {
                        println("data => ${it.name}, ${it.age}")
                    }
                }
                resp.end()
            }
        }
    
        routing("/user/:id") { req, resp ->  // restful 路由处理
            val id = req.params.id
            println("userid => $id")
            resp.end()
        }
    
        startListen(8888)  // 开始监听
    }
    

    其实关键问题不在代码本身,而在于可以很爽的进行开发,在大部分的情况下,你的 IDE 都会给予提示了。


    其实 Kotlin 服务端系列(含Ktor)讲到这里已经差不多没有什么额外的内容了,下面我会写一些 Kotlin 前端的开发,包括与 React 协同等。

    另外,按目前的经验,官方提供的新建项目的向导非常的不友好,每次写文,光是建项目,改配置就有一大堆的事情要做,我打算在近期开发一个全新的新建向导,以便帮助到各位。

    最后,本文所说的 wrapper 在哪里?点击此处访问我的Github 就好了!

    相关文章

      网友评论

        本文标题:Kotlin x Nodejs 封装方案,愉快的玩转服务端开发吧

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