I'm trying to learn how to use web sockets in Play 2.1, and I'm having trouble getting the web socket URL to work with my app's routing configuration. I started with a new Play application and the Play framework documentation on websockets.
Here is my conf/routes:
# Home page
GET / controllers.Application.index
# Websocket test site
GET /wstest controllers.Application.wstest
Then I added the wstest function to my controller class:
object Application extends Controller {
def index = Action {
Ok(views.html.index("Websocket Test"))
}
def wstest = WebSocket.using[String] { request =>
// Log events to the console
val in = Iteratee.foreach[String](println).mapDone { _ =>
Logger.info("Disconnected")
}
// Send a single 'Hello!' message
val out = Enumerator("Hello!")
(in, out)
}
}
However, so far, I can only access the websocket with the URL ws://localhost:9000/wstest (using the sample code at websocket.org/echo.html). I was looking at the sample/scala/websocket-chat app that comes with the Play framework, and it uses the routing configuration file to reference the websocket, like this:
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var chatSocket = new WS("#routes.Application.chat(username).webSocketURL()")
I tried replacing my websocket URL with #routes.Application.wstest.webSocketURL() and #routes.Application.wstest. The first one doesn't compile. The second one compiles, but the client and server don't exchange any messages.
How can I use my Play routing configuration to access this websocket? What am I doing wrong here?
Edit
Here is a screenshot of my compilation error, "Cannot find any HTTP Request Header here":
Without the compiler error it's hard to guess what might be the problem.
Either you have to use parens because of the implicit request, i.e. #routes.Application.wstest().webSocketURL(), or you have no implicit request in scope which is needed for the webSocketURL call.
Marius is right that there was no implicit request in scope. Here's how to get it in scope:
Update the index function in the controller:
def index = Action { implicit request =>
Ok(views.html.index("Websocket Test"))
}
Add the request as a curried parameter to index.scala.html:
#(message: String)(implicit request: RequestHeader)
#main(message) {
<script>
var output;
function init() {
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket("#routes.Application.wstest.webSocketURL()");
.
.
.
And now the RequestHeader is in scope.
Related
I'd like to test that a Http4s Client is being called from my class ClassUnderTest (to make a HTTP request) and that the request made contains the headers I expect.
In 0.18.x, I did something like below. Using a side effect to store the value of the headers so that I can make an assertion after the call. In the example below, f.execute(...) is expected to make a PUT with the client instance and I'm trying to record all request handling and storing the headers.
"Request has headers" >> {
var headers = List[String]()
val client = Client[IO](new Kleisli[IO, Request[IO], DisposableResponse[IO]](request => {
headers = request.headers.map(_.name.toString()).toList
Ok().map(DisposableResponse(_, IO.pure(())))
}), IO.pure(()))
val f = ClassUnderTest(client)
f.execute("example")
headers must_== List(
"Content-Type",
"X-Forwarded-For",
"Content-Length"
)
}
The real code is here if you're interested.
ClassUnderTest took a Client[IO] so I could get the above working.
class ClassUnderTest(client: http4s.Client[IO])
In Http4s 0.20.12, I had to change the signature to:
class ClassUnderTest(client: Resource[IO, http4s.Client[IO]])
...and now I can't figure out how to stub out the client for tests. I experimented with JavaNetClientBuilder but that doesn't help because I can get an instance of Client (after .create) and now I need a Resource[IO, http4s.Client[IO]].
How can I use a test double to stand in for the Client / Resource[F, Client[F]] so that I can test the requests it makes?
The testing page on the docs doesn't really help me. I want a test double, not to test all the functionality of the Service (I don't want to startup a server).
Assuming I have a folder foo with an index.html file in it and the following minimal (but most likely not functional) server code for Akka HTTP below:
object Server extends App {
val route: Route =
pathPrefix("foo") {
getFromDirectory("foo")
}
Http().bindAndHandle(route, "0.0.0.0", 8080)
}
index.html will correctly be served if I open http://localhost:8080/foo/index.html in a browser, but not if I open http://localhost:8080/foo or http://localhost:8080/foo/.
If this is even possible, how can I set up my Akka HTTP routes to serve index.html files within that location by default?
I know I could do the following:
val route: Route =
path("foo") {
getFromFile("foo/index.html")
} ~
pathPrefix("foo") {
getFromDirectory("foo")
}
But:
This only makes http://localhost:8080/foo work, not http://localhost:8080/foo/
This is very ad-hoc and does not apply globally: if I have a foo/bar/index.html file, the problem will be the same.
You can create the Route you are looking for by using the pathEndOrSingleSlash Directive:
val route =
pathPrefix("foo") {
pathEndOrSingleSlash {
getFromFile("foo/index.html")
} ~
getFromDirectory("foo")
}
This Route will match at the end of the path and feed up the index.html, or if the end doesn't match it will call getFromDirectory instead.
If you want to make this "global" you can make a function out of it:
def routeAsDir[T](pathMatcher : PathMatcher[T], dir : String) : Route =
pathPrefix(pathMatcher) {
pathEndOrSingleSlash {
getFromFile(dir + "/index.html")
} ~
getFromDirectory(dir)
}
This can then be called with
val barRoute = routeAsDir("foo" / "bar", "foo/bar")
Functional Akka Http
Side note: your code is completely functional. The elegance of the Directives DSL can be a bit misleading and convince you that you've accidentally strayed back into imperative programming style. But each of the Directives is simply a function; can't get more "functional" than that...
I`m trying to send post request to external url using play framework and scala. I want to add some parameters to the body also.
I want to send a post request to "http://www.posonlinedemo.tk" with parameters TransactionNo='T10000' and reqtype='T'
how could i do it?
here is my Action
def test(para:String) = Action {
val url: Option[String] = Some("http://www.posonlinedemo.tk")
url match {
case Some(url) => Redirect(url)
case None => NotFound("This URL leads nowhere. :(")
}
}
You can use the Play WS API.
As you can see in the documentation, it is that simple:
ws
.url(url)
.post(Map(
"TransactionNo" -> Seq("T10000"),
"reqtype" -> Seq("T")))
Don't forget to add ws to your library dependencies.
I'm slowly converting a REST API from Rails to Scala. I've got some methods working with play, but others have to fall back to the Rails server.
I want all requests to go through Play, but if they aren't implemented yet to redirect. Specifically if URL requested is play-app.com/api/v1/.* then it should be redirected to rails-app.com/api/v1/.*, with URL and all params in tact. I've tried this route:
GET /api/v1/*path
But now I don't know what to do with it.
If your route is
GET /api/v1/*path controllers.Api.v1(path: String)
Then your controller function would look something like this:
object Api extends Controller { request =>
val queryString: String = if(request.rawQueryString.nonEmpty) "?" + request.rawQueryString else ""
def v1(path: String) = Action {
TemporaryRedirect("rails-app.com/api/v1/" + path + queryString )
}
}
I'm evaluating the possibility of using Play2-mini with Scala to develop a service that will sit between a mobile client and existing web service. I'm looking for the simplest possible example of a piece of code where Play2-mini implements a server and a client. Ideally the client will use Akka2 actors.
With this question, I'm trying to find out how it is done, but also to see how Play2-Mini and Akka2 should co-operate. Since Play2-Mini appears to be the replacement for the Akka HTTP modules.
Play2-mini contains the following code example, in which I created two TODO's. If someone can help me with some sample code to get started, I will be really grateful.
package com.example
import com.typesafe.play.mini._
import play.api.mvc._
import play.api.mvc.Results._
object App extends Application {
def route = {
case GET(Path("/testservice")) & QueryString(qs) => Action{ request=>
println(request.body)
//TODO Take parameter and content from the request them pass it to the back-end server
//TODO Receive a response from the back-end server and pass it back as a response
Ok(<h1>Server response: String {result}</h1>).as("text/html")
}
}
}
Here's the implementation of your example.
Add the following imports:
import play.api.libs.ws.WS
import play.api.mvc.BodyParsers.parse
import scala.xml.XML
Add the following route:
case GET(Path("/testservice")) & QueryString(qs) => Action{ request =>
Async {
val backendUrl = QueryString(qs,"target") map (_.get(0)) getOrElse("http://localhost:8080/api/token")
val tokenData = QueryString(qs,"data") map (_.get(0)) getOrElse("<auth>john</auth>")
WS.url(backendUrl).post(XML loadString tokenData).map { response =>
Ok(<html><h1>Posted to {backendUrl}</h1>
<body>
<div><p><b>Request body:</b></p>{tokenData}</div>
<div><p><b>Response body:</b></p>{response.body}</div>
</body></html>).as("text/html") }
}
}
All it does, is forwarding a GET request to a back-end serivce as a POST request. The back-end service is specified in the request parameter as target and the body for the POST request is specified in the request parameter as data (must be valid XML). As a bonus the request is handled asynchronously (hence Async). Once the response from the back-end service is received the front-end service responds with some basic HTML showing the back-end service response.
If you wanted to use request body, I would suggest adding the following POST route rather than GET (again, in this implementation body must be a valid XML):
case POST(Path("/testservice")) & QueryString(qs) => Action(parse.tolerantXml){ request =>
Async {
val backendUrl = QueryString(qs,"target") map (_.get(0)) getOrElse("http://localhost:8080/api/token")
WS.url(backendUrl).post(request.body).map { response =>
Ok(<html><h1>Posted to {backendUrl}</h1>
<body>
<div><p><b>Request body:</b></p>{request.body}</div>
<div><p><b>Response body:</b></p>{response.body}</div>
</body></html>).as("text/html") }
}
}
So as you can see, for your HTTP Gateway you can use Async and play.api.libs.ws.WS with Akka under the hood working to provide asynchronous handling (no explicit Actors required). Good luck with your Play2/Akka2 project.
Great answer by romusz
Another way to make a (blocking) HTTP GET request:
import play.api.libs.ws.WS.WSRequestHolder
import play.api.libs.ws.WS.url
import play.api.libs.concurrent.Promise
import play.api.libs.ws.Response
val wsRequestHolder: WSRequestHolder = url("http://yourservice.com")
val promiseResponse: Promise[Response] = wsRequestHolder.get()
val response = promiseResponse.await.get
println("HTTP status code: " + response.status)
println("HTTP body: " + response.body)