Play ReactiveMongo - exception when trying to find one document
Asked Answered
P

1

8

I've started using Play and the Play-ReactiveMongo plugin and testing for a 404 response on a GET "document by id" scenario. Unfortunately instead of Play returning a 404 NotFound response I get this exception:

java.util.NoSuchElementException: JsError.get
        at play.api.libs.json.JsError.get(JsResult.scala:11) ~[play_2.10.jar:2.1.1]
        at play.api.libs.json.JsError.get(JsResult.scala:10) ~[play_2.10.jar:2.1.1]
        at play.modules.reactivemongo.json.collection.JSONGenericHandlers$StructureBufferWriter$.write(jsoncollection.scala:44) ~[play2-reactivemongo_2.10-0.9.jar:0.9]
        at play.modules.reactivemongo.json.collection.JSONGenericHandlers$StructureBufferWriter$.write(jsoncollection.scala:42) ~[play2-reactivemongo_2.10-0.9.jar:0.9]
        at reactivemongo.api.collections.GenericQueryBuilder$class.reactivemongo$api$collections$GenericQueryBuilder$$write(genericcollection.scala:323) ~[reactivemongo_2.10-0.9.jar:0.9]
        at reactivemongo.api.collections.GenericQueryBuilder$class.cursor(genericcollection.scala:333) ~[reactivemongo_2.10-0.9.jar:0.9]

The getById function below successfully returns a single document if the id parameter matches an existing document, but an exception on the line "one[JsValue]" if document not found.

Route file:

GET       /items/:id            controllers.ItemsController.getById(id: String)

Controller object:

object ItemsController extends Controller with MongoController {

    def itemsCollection: JSONCollection = db.collection[JSONCollection]("items")

    def getById(id: String) = Action {
       Async {  

              val query = Json.obj("_id" -> Json.obj("$oid" ->id))
              val futureItem = itemsCollection.
                find(query).
                one[JsValue]

              futureItem.map {
                case Some(item) => Ok(item)
                case None => NotFound(Json.obj("message" -> "No such item"))
              }
        }
    }
    }

Maybe I missed something in the docs?

There is partial example documented here:

https://github.com/sgodbillon/reactivemongo-demo-app#simple-query

The mandubian coast-to-coast example handles BadRequest as well NotFound scenario, but code is maybe out of date as it doesn't use the newer looking find(...).one[...] semantics?

http://mandubian.com/2013/01/13/JSON-Coast-to-Coast/#action-get

Palaeozoology answered 15/6, 2013 at 19:6 Comment(5)
instead of futureItem.map should not it be futureItem match ?Haggerty
nico_ekito, that suggestion fails to compile. The exception happens earlier anyway, Play reports it for the line "one[JsValue]". Thanks for trying.Palaeozoology
FYI, future.map { } and future.map(_ match {}) are equivalent.Pectize
@JulienLafont Thanks, I did not know :-)Haggerty
I've asked the question in the ReactiveMongo Google group too: groups.google.com/forum/#!topic/reactivemongo/yaJUTpcM_xYPalaeozoology
P
2

Turns out the ID needs to be a valid ObjectId, e.g. 24 characters and no illegal tokens.

  • If the ID is valid but does not reference an existing document then I get a 404 as expected.
  • If the ID format is invalid (e.g. only 12 characters or contains illegal tokens like '@') then I get an Exception.

When I compare behaviour with an equivalent Node.js + Mongoose app results are very similar.

For example if deliberately querying with a malformed 12 character ID I get this stacktrace in Node:

{ message: 'Cast to ObjectId failed for value "51bded70543f" at path "_id"',
  name: 'CastError',
  type: 'ObjectId',
  value: '51bded70543f',
  path: '_id' }

Not sure if this exception is the underlying error in the Play app too but it gave enough of a clue. The answer would seem to be pre-validate IDs before calling find(query).one[T].

Palaeozoology answered 16/6, 2013 at 17:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.