Ive been using the low level API but taking another look at the DSL . I have a number of actors that all base on a class called EndpointActor and have some basic facilities to handle errors, problems in auth and so on in custom ways. I want to know if this is possible in the DSL. The base class of my actors is like this:
abstract class EndpointActor(val services: NodeServices, val replyTo: ActorRef, val requestId: UUID, val request: HttpRequest) extends Actor { val start: Long = System.nanoTime protected val log = Logging(context.system, this) context.setReceiveTimeout(EndpointActor.DEFAULT_TIMEOUT) override def receive: Receive = { case ReceiveTimeout => onTimeout() } protected def bodyAsJsValue: Future[Option[JsValue]] = { import context.dispatcher request.entity.toStrict(3.seconds)(services.materializer).map(e => { try { val dataString = e.data.utf8String if (dataString.isEmpty) None else Option(Json.parse(e.data.utf8String)) } catch { case e: JsonParseException => badRequest(JsString("Cannot parse request body: " + e.getMessage)) None } }) } protected def withValidBody[A](function: Option[A] => Unit)(implicit reads: Reads[A]): Unit = { require(function != null) import context.dispatcher bodyAsJsValue.map { case None => function.apply(None) case Some(json) => json.validate[A] match { case s: JsSuccess[A] => function.apply(Option(s.get)) case e: JsError => val errorsJson = JsError.toJson(e) badRequest(errorsJson, errorsJson) } // todo check parsing issues } recover { case e: Exception => onException(e) } } protected def withRequiredBody[A](function: A => Unit)(implicit reads: Reads[A]): Unit = { require(function != null) withValidBody[A]({ case None => badRequest(JsString("Body Content Missing")) case Some(decoded) => function(decoded) })(reads) } protected def okSuccess[A](content: JsValue): Unit = { request.discardEntityBytes(services.materializer) val response = EndpointResponse.success(requestId, content) replyTo.tell(response.toHttpResponse, self) context.stop(self) if (log.isDebugEnabled) log.debug(Json.prettyPrint(logDetail(response))) // todo Change to debug mode } protected def okError[A](content: JsValue): Unit = { val response = EndpointResponse.error(requestId, content) replyTo.tell(response.toHttpResponse, self) if (log.isDebugEnabled) log.debug(Json.prettyPrint(logDetail(response))) // todo Change to debug mode context.stop(self) } protected def forbidden(content: JsValue): Unit = { request.discardEntityBytes(services.materializer) val response = EndpointResponse.forbidden(requestId, content) replyTo.tell(response.toHttpResponse, self) if (log.isDebugEnabled) log.debug(Json.prettyPrint(logDetail(response))) context.stop(self) } protected def badRequest[A](content: JsValue, logInfo: JsValue = JsNull): Unit = { // todo The ops people should be monitoring for many bad requests because it could indicate hacking attempts request.discardEntityBytes(services.materializer) // drop any content passed val response = EndpointResponse.badRequest(requestId, content) replyTo.tell(response.toHttpResponse, self) if (log.isDebugEnabled) log.debug(Json.prettyPrint(logDetail(response, logInfo))) context.stop(self) } protected def notFound(): Unit = { request.discardEntityBytes(services.materializer) val response = EndpointResponse.notFound(requestId, request) replyTo.tell(response.toHttpResponse, self) if (log.isDebugEnabled) log.debug(Json.prettyPrint(logDetail(response))) context.stop(self) } protected def onTimeout(): Unit = { request.discardEntityBytes(services.materializer) val response = EndpointResponse.timeout(requestId, request) replyTo.tell(response.toHttpResponse, self) log.error(Json.prettyPrint(logDetail(response))) context.stop(self) } protected def onException(ex: Exception): Unit = { request.discardEntityBytes(services.materializer) // drop any content passed val response = EndpointResponse.internalError(requestId, request) replyTo.tell(response.toHttpResponse, self) log.error(ex, Json.prettyPrint(logDetail(response))) context.stop(self) } private def logDetail(endpointResponse: EndpointResponse, logInfo: JsValue = JsNull): JsValue = { Json.obj( "type" -> JsString(this.getClass.getName), "requestUID" -> JsString(requestId.toString), "elapsed" -> JsNumber(TimeFrame.elapsedSeconds(start)), "uri" -> JsString(request.getUri.toRelative.toString), "logInfo" -> logInfo, "response" -> endpointResponse.toJson, "debugInfo" -> debugInfo ) } protected def debugInfo: JsValue } This forms the base per-request actor. for my application. As each request comes in, one of the sub-classes is started up and handles it and calls one of the methods. The thing is the base class methods make it really easy on the individual actors because they just have to call those methods to set the response type and return a formatted message. The real reason for errors is hidden from the web request because that gives info for hackers to use. What I would like to know is if there is some generic way in the DSL to say "If the entity doesn't decode, send back bad request with json in this format" without putting a handler on every endpoint in the DSL and without embedding a ton of application logic in the DSL. Would rather the endpoint not need to worry about these little detail. The DSL seems to be a bit rigid that whenever it cant decode an entity, it returns a validation rejection but I have no control over the HTTP code or the content of the rejection. I don't want to tell users why their request is bad so they can figure out how to hack the API. As an example, consider this demo code: object DSLServerNode extends App { object Foo { implicit val fooFormat: Format[Foo] = Json.format[Foo] } case class Foo(bar: String) implicit val system: ActorSystem = ActorSystem() implicit val mat: ActorMaterializer = ActorMaterializer() Http().bindAndHandle(route, "127.0.0.1", 8000) StdIn.readLine("Hit ENTER to exit") Await.ready(system.terminate(), Duration.Inf) def route(implicit mat: Materializer): Route = { import Directives._ import PlayJsonSupport._ pathSingleSlash { post { entity(as[Foo]) { foo => complete { foo } } } } } } If the JSON fails to decode to a FOO, the system sends back an error to the HTTP request but I don't have control over the content of that error or the HTTP response code. Similarly inside the route if foo had an illegal string according to the business logic, for example lets say white space is disallowed, then I would want to send back a bad request, not complete it with HTTP OK. Suggestions? Thanks for your time. -- >>>>>>>>>> Read the docs: http://akka.io/docs/ >>>>>>>>>> Check the FAQ: >>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html >>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user --- You received this message because you are subscribed to the Google Groups "Akka User List" group. To unsubscribe from this group and stop receiving emails from it, send an email to akka-user+unsubscr...@googlegroups.com. To post to this group, send email to akka-user@googlegroups.com. Visit this group at https://groups.google.com/group/akka-user. For more options, visit https://groups.google.com/d/optout.