object Routes extends RestHelper with Logger { // response builder implicit def cvt:JxCvtPF[ResponseItem] = { case (XmlSelect, response, request) => response.toXml case (JsonSelect, response, request) => response.toJson } private val PREFIX:String = "api" private var _services:mutable.ListMap[String, BaseService] = null def registerService(service:BaseService, alias:String=null) { if (null == _services) _services = new mutable.ListMap[String, BaseService]() val key:String = if (null != alias) alias else service.getClass.getName.split("\\.").toList.last.replace("Service$", "") _services += key -> service } /* *** standard restful routes */ serveJx { // GET /api/service/index.{xml|json} case Get(PREFIX :: StringValue(service) :: "index" :: Nil, _) => invokeApi(service, "index") } serveJx { // GET /api/service/1.{xml|json} case Get(PREFIX :: StringValue(service) :: LongValue(id) :: Nil, _) => invokeApi(service, "get", id) } serveJx { // GET /api/service/api.{xml|json} case Get(PREFIX :: StringValue(service) :: StringValue(api) :: Nil, _) => invokeApi(service, api) } serveJx { // GET /api/service/api/1.{xml|json} case Get(PREFIX :: StringValue(service) :: StringValue(api) :: LongValue(id) :: Nil, _) => invokeApi(service, api, id) } serveJx { // POST /api/service.{xml|json} case Post(PREFIX :: StringValue(service) :: Nil, _) => invokeApi(service, "create") } serveJx { // POST /api/service/api.{xml|json} case Post(PREFIX :: StringValue(service) :: StringValue(api) :: Nil, _) => invokeApi(service, api) } serveJx { // POST /api/service/api/1.{xml|json} case Post(PREFIX :: StringValue(service) :: StringValue(api) :: LongValue(id) :: Nil, _) => invokeApi(service, api, id) } serveJx { // PUT /api/service/1.{xml|json} case Put(PREFIX :: StringValue(service) :: LongValue(id) :: Nil, _) => invokeApi(service, "update", id) } serveJx { // DELETE /api/service/1.{xml|json} case Delete(PREFIX :: StringValue(service) :: LongValue(id) :: Nil, _) => invokeApi(service, "delete", id) } private def invokeApi(service:String, api:String, id:Long=0):Box[ResponseItem] = { val serviceName:String = StringHelpers.camelify(service) val methodName:String = StringHelpers.camelifyMethod(api) info("processing api " + service + ":" + api + " as " + serviceName + "[Service]:" + methodName) if (!_services.contains(serviceName)) return Full(Failed("unknown service " + service)) _services(serviceName) match { case serviceHandler:BaseService => { serviceHandler.getClass.getMethods.foreach((method:Method) => { if (methodName == method.getName) { val result:Object = if (0 == id) method.invoke(serviceHandler) else method.invoke(serviceHandler, id.asInstanceOf[AnyRef]) result match { case response:ResponseItem => return Full(response) case _ => return Empty } } }) return Full(Failed("unknown API " + service + ":" + api)) } case _ => Full(Failed("invalid API configuration, service is not a BaseService")) } } }
Once the routes handler (above) is define, you need to register the service implementation(s) to the routes handler and register the routes handler to Lift's dispatch table, this is done in lift's boot sequence, typically defined in Boot.scala. for example:
Routes.registerService(SessionService) Routes.registerService(UserService) . . . LiftRules.dispatch.append(Routes)
Naturally, you can extend this rest handler to handle custom restful routes, using the serveJx or serve directives, this is explained in more details here
Please note that a bug in scala is preventing from bundling all those serveJx case statements together, thus, the DRYlessness.
please note a breaking change introduced by liftweb 2.3, details here http://groups.google.com/group/liftweb/browse_thread/thread/a1687794b26a63b8
ReplyDelete