akka / akka/akka-http

Cross-domain Ajax request error

Open
#149 1 comment 0 reactions 0 assignees View on GitHub
1 - triaged t:server
Dominant language
Scala
Stars
1.4k
Forks
584
Avg merge
14h 33m
Merged PRs (30d)
24

Description

**Issue by [danicheg](https://github.com/danicheg)**
_Friday Jun 17, 2016 at 10:14 GMT_
_Originally opened as https://github.com/akka/akka/issues/20796_

---

_First of all, sorry for the long read._

I use [akka-http-cors](https://github.com/lomigmegard/akka-http-cors) to enable cross origin requests.
Assume on the server side we create users and keep them in Vector:

``` scala
import scala.concurrent.duration._
import akka.actor._
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.model.{HttpResponse}
import akka.http.scaladsl.server.{Directives, RejectionHandler, Route}
import akka.stream.ActorMaterializer
import akka.util.Timeout
import ch.megard.akka.http.cors.CorsDirectives._
import ch.megard.akka.http.cors.{CorsDirectives, CorsSettings}
import scala.concurrent.{ExecutionContext, Future}
import spray.json.DefaultJsonProtocol

trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
implicit val userFormat = jsonFormat3(User)
}

case class User(id: String, name: String, password: String)

class UserService(implicit val executionContext: ExecutionContext) {
var users = Vector.empty[User]

def createUser(user: User): Future[Option[String]] = Future {
users.find(_.id == user.id) match {
case Some(u) => None
case None =>
users = users :+ user
Some(user.id)
}
}
}

trait UserResources extends Directives with JsonSupport {

implicit def executionContext: ExecutionContext

lazy val userService: UserService = new UserService
val settings = CorsSettings.defaultSettings.copy(allowCredentials = false)

val routes: Route = handleRejections(CorsDirectives.corsRejectionHandler) {
cors(settings) {
handleRejections(RejectionHandler.default) {
(path("cors-issue") & post & entity(as[User])) { user =>
userService.createUser(user)
complete(HttpResponse(201))
}
}
}
}
}

object Main extends App with UserResources{
implicit val system = ActorSystem("test-service")
implicit val materializer = ActorMaterializer()

implicit val executionContext = system.dispatcher
implicit val timeout = Timeout(10 seconds)

val api = routes

Http().bindAndHandle(handler = api, interface = "0.0.0.0", port = 5000) map { binding =>
println(s"REST interface bound to ${binding.localAddress}")
}
}
```

`build.sbt`:

``` scala
enablePlugins(JavaServerAppPackaging)

name := "cors-issue"
version := "0.1"
scalaVersion := "2.11.8"
scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")

libraryDependencies ++= {
val AkkaVersion = "2.4.7"
val AkkaHttpVersion = "2.4.7"

Seq(
"com.typesafe.akka" %% "akka-slf4j" % AkkaVersion,
"com.typesafe.akka" %% "akka-http-core" % AkkaVersion,
"com.typesafe.akka" %% "akka-http-experimental" % AkkaHttpVersion,
"com.typesafe.akka" %% "akka-http-spray-json-experimental" % AkkaVersion,
"ch.megard" %% "akka-http-cors" % "0.1.2"
)
}

Revolver.settings
```

And if we now test it with cURL:

``` bash
$ curl -v -H "Content-Type: application/json" \
> -X POST "http://localhost:5000/cors-issue" \
> -d '{"id": "123", "name": "123", "password": "123"}'
```

We will get the response:

``` bash
* Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> POST /cors-issue HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.45.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 47
>
* upload completely sent off: 47 out of 47 bytes
< HTTP/1.1 201 Created
< Server: akka-http/2.4.7
< Date: Fri, 17 Jun 2016 10:08:32 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact
```

That's OK. But if we try to send POST requests using Ajax:

``` javascript
jQuery.ajax({
type: "POST",
url: "http://localhost:5000/cors-issue",
contentType: "application/json",
crossDomain: true,
data: {"id": id, "name": name, "password": password},
dataType: "json",
success: function(status) {
console.log("OK: " + status)
},
error: function(xhr,status,error) {
console.log("NOT OK: " + xhr + " " + status + " " + error)
}
});
```

We will get an error:
`> POST http://localhost:5000/cors-issue 400 (Bad Request)`

Contributor guide

Open the contributing guide

Research direction

Start with the UserResources routes in the posted example and the build.sbt dependencies, then reproduce the request with the curl command and the jQuery.ajax call. Inspect the CORS handling and the differing request payloads; done means the Ajax POST to /cors-issue is accepted and returns the expected 201 response.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, jquery, scala
Domain
api, backend, web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.