scala - EssentialAction: How to Get the Body of a Request without Parsing It -
given following essentialaction
...
object mycontroller extends controller { ... def hastoken(action: token => essentialaction) = essentialaction { request => ... // doesn't compile val body = request.body match { case json: jsvalue => json.tostring case _ => "" } // calculate hash body content here ... } // here authenticated action def getuser(userid: strign) = hastoken { token => action(parse.json) { request => request.body.validate[user] match { ... } } } }
... how body of request without parsing it?
i don't want , don't need parse request body in hastoken
since body going parsed in action getuser
. need raw content of body calculate hash.
the code in hastoken
doesn't compile because request
of type requestheader
whereas need request
, defines body
.
will work ?
object mycontroller extends controller { // hastoken action def authenticate(action: token => essentialaction) = essentialaction { requestheader => // ... execute logic verify authenticity using requestheader } // action validate tampering of request body , validity of json def validate[a](action: token => request[a]) = action(parse.json) { request => val body = request.body body match { case json: jsvalue => json.tostring case _ => "" } // calculate hash body content here body.validate[user] match { // ... } } def getuser(userid: strign) = authenticate { token => validate { user => //.... continue } } }
- authentication uses requestheader
- validation uses request body. (bonus: body parsed once)
edit:
question #1: don't want validate body in validate... since need generic validation mechanism used everywhere regardless of content type (e.g. user, message, etc.).
how adding type param (so made generic):
def validate[a, b](action: token => request[a])(implicit reads: reads[b]) = action(parse.json) { request => // ... }
question #2: furthermore, if token validation fails, body don't have processed (that's important in case of file upload, has performed if , if validation succeeded). that's way, in opinion, best option read raw content of body in validate.
this can achieved:
def validate[a, b](action: token => request[a])(implicit reads: reads[b]) = action(parse.json) { request => val body = request.body body match { case json: jsvalue => json.tostring case _ => "" } // calculate hash body content here , figure out if body tampered if (bodyisnottampered) { body.validate[b] match { // ... } } else { // log , return future.successful(badrequest) } }
edit 3: full solution:
import play.api.libs.json.{json, jsvalue, format} object compilationutils { class token case class user(name: string) implicit val userformat = json.format[user] def authenticate = new token // authentication logic def istampered(body: jsvalue) = { val bodyasstr: string = json.stringify(body) // calculate hash body content here false } } object mycontroller extends controller { import compilationutils._ // hastoken action def authenticate(action: token => essentialaction) = essentialaction { requestheader => action(authenticate)(requestheader) // execute logic verify authenticity using requestheader } // action validate tampering of request body , validity of json def validate[a, b](request: request[a])(implicit formata: format[a], formatb: format[b]): either[result, b] = { val body = request.body val bodyasjsvalue = json.tojson(body) if (!istampered(bodyasjsvalue)) { bodyasjsvalue.validate[b].fold( valid = res => right(res), invalid = err => left(badrequest(err.tostring)) ) } else { left(badrequest) // request tampered } } def getuser(userid: string) = authenticate { token => action(parse.json) { request => validate(request).fold( badreq => badreq, user => // continue... ok("") ) } } }
Comments
Post a Comment