使用Kotlin + Jersey + Jetty + MongoDB创建可扩展的RESTful API - Andrew


这种组合可以像其他任何servlet应用程序一样扩展,同时也可以部署到其他服务器,例如Tomcat。与Node.js之类的东西相比,我更喜欢服务器端的Kotlin。尽管它需要更多的初始设置,但您以后可以使用它,因此会大大受益。对我们来说,最重要的两件事是正确的多线程支持以及正确构建代码的固有需求。
我们使用Jetty为公司中的多个Web应用程序提供服务。为了易于使用和部署,Jetty会永久安装在我们的服务器上,我们不会将其嵌入到webapp本身中。

1.创建一个Kotlin项目。我们使用Gradle,但是您可以根据需要使用Maven。您也可以使用我的模板

2.除了通常的依赖关系之外,这还需要依赖关系的列表:

dependencies {    
    compile 'org.mongodb:mongodb-driver-sync:3.10.0'    
    compile 'org.litote.kmongo:kmongo:3.9.2'    
    compile 'org.litote.kmongo:kmongo-id-jackson'    
    compile 'org.litote.kmongo:kmongo-id:3.9.2'
    compile 'org.glassfish.jersey.core:jersey-server:2.30'    
    compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.30'
    compile 'org.glassfish.jersey.inject:jersey-hk2:2.30'    
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.30'
}

3.创建一个web.xml在src/main/webapp/WEB-INF/web.xml
4.使用此代码段填充您的web.xml。将%%替换为您的信息。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
    <display-name>%%YourProjectName%%</display-name>
    <servlet>
        <servlet-name>Jersey REST Service</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>%%your.package.here%%</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey REST Service</servlet-name>
        <url-pattern>/api
/*</url-pattern>
    </servlet-mapping>
</web-app>

5.我们需要注册@Provider的KMongo。这将确保JSON对象与KMongo我们接下来创建的数据类之间的转换是无缝的。创建一个KMongoProvider.kt并使用以下代码:

@Provider
class KMongoProvider : ContextResolver<ObjectMapper> {
    override fun getContext(type: Class<*>): ObjectMapper {
        return KMongoJackson.mapper
    }
}

object KMongoJackson {
    val mapper = jacksonObjectMapper()

    init {
        mapper.registerModule(IdJacksonModule())
    }
}

6.让我们连接到数据库并创建一个数据模型:

class Connect {
    companion object {
        val client = KMongo.createClient()
        val database = client.getDatabase("kotlin-example")
    }
}

data class Dog (
        @BsonId var _id: StringId<Dog> = newStringId(),
        var owner: String,
        var name: String,
        var bornAt: Int,
        var lastVaccineAt: Int?
) {
    companion object

    fun collection(): MongoCollection<Dog> {
        return Connect.database.getCollection<Dog>("dogs")
    }
}

7.现在我们需要为我们的Dog类提供资源。它将告诉Jersey如何与模型进行交互,并将为API访问公开模型:

@Path("dogs")
@Produces(APPLICATION_JSON)
class DogResource {

    @GET
    fun listDogs(): MongoIterable<Dog> {
        return Dog.collection().find().map { it }
    }

    @POST
    fun createDog(token: Dog) {
        Dog.collection().save(token)
    }

    @GET
    @Path("{id}")
    fun getDog(@PathParam("id") id: String): Dog? {
        return Dog.collection().findById(id)
    }

    @PUT
    @Path("{id}")
    fun updateDog(@PathParam("id") id: String, dog: Dog) {
        dog._id = StringId(id)
        Dog.collection().replaceById(id, dog)
    }

    @DELETE
    @Path("{id}")
    fun deleteDog(@PathParam("id") id: String): Boolean {
        return Dog.collection().deleteById(id).deletedCount > 0
    }

}

该listDogs()方法不使用任何参数,而是使用我们为DogResource定义的路径。createDog()方法输入参数是JSON,与数据模型匹配但没有_id密钥。尝试发送错误格式的,JSON以查看其行为。该get / update / delete方法适用于*/dogs/{id}/。

8.我使用下面列出的扩展功能轻松地通过id查找/替换内容。创建一个DBUtility.kt并将函数放入其中。

fun <T> MongoCollection<T>.findById(id: String): T? {
    return this.findOneById(StringId<T>(id))
}

fun <T> MongoCollection<T>.deleteById(id: String): DeleteResult {
    return this.deleteOneById(StringId<T>(id))
}

fun <T> MongoCollection<T>.replaceById(id: String, newObject: T): UpdateResult {
    return this.replaceOne(KMongoUtil.idFilterQuery(StringId<T>(id)), newObject)
}

9.现在进行测试。我使用Postman发送POST请求到localhost:8080/api/dogs/。内容如下:

{
    "owner": "John Doe",
    "name": "Good Boy",
    "bornAt": 1583158516
}

现在,您可以在几分钟内添加任意数量的资源,而无需为此做太多事情。您还可以使用自定义路径并定义所需的任何方法,Jersey将始终为您处理解析和实际servlet创建。