You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@esme.apache.org by David Pollak <fe...@gmail.com> on 2009/02/04 21:45:53 UTC

Work on the ESME REST APIs (and some stuff that will be rolled into Lift)

Folks,
I'm been working on the updates to the ESME REST APIs.

I've reduced the call cycle to something that looks like:

   1. A request is dispatched based on the URL
   2. The target of the dispatch returns a Box[T] where T is the type of the
   thing that will be calculated if it can be
      1. T might be Boolean, Int, User, etc.
      2. It's in a Box because it perhaps cannot be calculated (e.g., not
      enough permissions, etc.)
      3. The Box can be a Failure or even a FailureParam.  In the case of
      FailureParam, the param is the HTTP return code
   3. The result is put in an IntermediateAnswer along with a function that
   can convert from T to the expected HTTP response type (e.g., XML or JSON)
   4. The IntermediateAnswer is queried in order to build the actual
   response.

With a nice dose of implicit, the code becomes very clean:

  val dispatch: RetType = {
    case Req("rest" :: "status" :: Nil, "", GetRequest) => status()
    case Req("rest" :: "login" :: Nil, "", PostRequest) => login()
    case Req("rest" :: "logout" :: Nil, "", GetRequest) => logout()
    case Req("rest" :: "message" :: Nil, "", GetRequest) => getMsgs()
  }

And let's see some implementations:
  def status()  = User.currentUser ?~ "No Session" ~> 400

  def logout() = {
    User.logUserOut()
    true
  }

In status(), if there's no user logged in, we create a "No Session" error
message and return a 400.

In logout(), we just return true.

Now, the implicit magic comes from:
  implicit def boolToResp(v: Boolean, t: RequestedResponse): LiftResponse =
(v, t) match {
    case (v, XMLReqResponse) => new XmlResponse(<resp answer={v.toString}/>)
    case (v, JSONReqResponse) => JSONResponse(v)
  }

  implicit def userToResp(v: User, t: RequestedResponse): LiftResponse = (v,
t) match {
    case (v, XMLReqResponse) => new XmlResponse(v.toXml)
    case (v, JSONReqResponse) => JSONResponse(v.asJs)
  }

The code will automatically pick up these methods that convert from the
object type to the appropriate response type.  One of these "views" must
exist for each type that is returned from a dispatched method.

As I finish this stuff up, I'll roll it into some generic Lift classes.

Thanks,

David

-- 
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Git some: http://github.com/dpp

Re: Work on the ESME REST APIs (and some stuff that will be rolled into Lift)

Posted by Vassil Dichev <vd...@apache.org>.
Hi David,

That's pretty amazing, given that I'm attempting to integrate a
Twitter-compatible REST API in ESME. I've encountered some similar
problems, which you may have solved already.

At first I thought about generating XML and then converting to JSON-
my line of thinking was that it's fairly easy to manipulate XML in
Scala. Eventually I reached the conclusion this solution would be
flawed, since XML lacks some information necessary for JSON transfer
(it's just not clean overall).

Then I decided to use some internal data structure, which I could
convert to either XML or JSON. I currently have methods to convert a
simple Map/List to XML or JSON. My questions are:

-how do you find out the expected response type- XML or JSON?
-how do the magic methods toXml and asJs work? I encountered such
methods in Lift, but they didn't seem generic enough at first glance.

Depending on the answers, should I wait a bit to see if I can reuse
some if this cool new stuff or is there little overlap?

Cheers,
Vassil

P.S. The new HttpAuthProtected in Lift is quite cool and I definitely
intend to use it for the HTTP Basic auth Twitter requires.