简体   繁体   中英

Scala + Reactivemongo: Controling custom readers/writers for REST-API

I am working on a REST-API implemented in Scalatra and using reactivemongo. My persistent model is implemented using case classes and a thin repository layer uses the common approach for bson<->class mapping via DocumentReader/DocumentWriter (via implicit converters in case class companion object).

case class Address(street: String, number: String, city: String, zip: String)
object Address{

implicit val addressHandler = Macros.handler[Address]
implicit val addressFmt = Json.format[Address]
}

The first formatter maps bson to case classes the second converts to json (the output format of the REST-API).

This is all fine and I am quite happy with how nicely everything integrates.

In many cases though, I don't need to operate on the domain objects (case class instances) and just want to stream the data coming from the data base write into the http response. All the intermediate conversions are overhead in those scenarios. I also want to control which fields are exposed (I once used Yoga and Jackson in a Java project).

Possible solutions for this would be to:

  • have a generic repository that simply converts to a Map structure as intermediate format.
  • control the implicit converters available to the driver on a per-query basis and write case classes for different "views"
  • use BSONDocument as intermediate format and make the REST layer understand BSON via a bson=>string conversion.

I wonder what is the best approach and if anybody has some experience with that particular scenario. Maybe I am even missing out another good option? Feedback is very welcome.

To control which fields are exposed, you probably have to write a Readers and Writers. Below is an example extracted from this project https://github.com/luongbalinh/play-mongo .

import java.time.ZonedDateTime

case class User(override val id: Option[Long] = None,
            firstName: String,
            lastName: String,
            age: Int,
            active: Boolean,
            createdDate: Option[ZonedDateTime] = None,
            updatedDate: Option[ZonedDateTime] = None
             ) extends IdModel[User] {
  override def withNewId(id: Long): User = this.copy(id = Some(id))
}

object User {

import play.api.libs.functional.syntax._
import play.api.libs.json._

implicit val userReads: Reads[User] = (
(JsPath \ "id").readNullable[Long] and
  (JsPath \ "firstName").read[String] and
  (JsPath \ "lastName").read[String] and
  (JsPath \ "age").read[Int] and
  (JsPath \ "active").read[Boolean] and
  (JsPath \ "createdDate").readNullable[ZonedDateTime] and
  (JsPath \ "updatedDate").readNullable[ZonedDateTime]
)(User.apply _)

implicit val userWrites: Writes[User] = (
(JsPath \ "id").writeNullable[Long] and
  (JsPath \ "firstName").write[String] and
  (JsPath \ "lastName").write[String] and
  (JsPath \ "age").write[Int] and
  (JsPath \ "active").write[Boolean] and
  (JsPath \ "createdDate").writeNullable[ZonedDateTime] and
  (JsPath \ "updatedDate").writeNullable[ZonedDateTime]
)(unlift(User.unapply))
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM