So I have a route structure something like this
pathPrexix("root"){
concat {
path("path") {
get {
parameters("someId".as[String], 'fixedValue ! "requiredValue") { params =>
}
}
},
path(Segment) { extractedValue =>
.....
}
}
}
If the user ends a request to the /root/path endpoint with the incorrect query parameters (either someId missing or fixedValue not equal to value) then the request will be routed further on to the next route, root/Segment. extractedValue would in this case be path which would fail and send the user back error handled by the second route.
The preferred behaviour would be to tell the user that they either missed a query parameters or that the query parameter must be one of the given values. Is there any way to make sure that happen?
If I move the second path above the first, it will capture all requests sent.
You just need to complete with an appropriate error code if the get does not match:
path("path") {
concat(
get {
parameters("someId".as[String], 'fixedValue ! "requiredValue") { params =>
}
},
complete(StatusCodes.NotFound)
)
},
You could put additional information in the reply message, but it would be non-standard and therefore would require the client to be aware of it.
Related
I’d like to require a header in my akka-http routes and can do so via
val route = headerValueByName("foo") { foo =>
pathPrefix("path") {
get {
...
} ~ ...
}
}
However, now any requests that don't match a path will get rejected with 400 (missing header) and not 404.
Is there a neat way to get around this without repeatedly moving headerValueByName after the path matchers?
That is, is there a way to only apply an outer directive ( headerValueByName) and its rejections if the inner path and method matchers are successful?
You don't specify what you want to do in case the header is not specified, so I'll asume you want to return 400 (Bad request).
A possible solution is to use the optionalHeaderValueByName directive and then complete the request with the specified error, for example:
val route = optionalHeaderValueByName("foo") { optionalHeader =>
optionalHeader map { header =>
// rest of your routes
} getOrElse complete(StatusCodes.BadRequest)
}
I'm designing a REST service using Akka-HTTP 2.0-M2 and have come across a situation where I'd like to supply additional headers which are dependent upon the reply of the queried Actor.
Currently, I have the following...
val route = {
path("oncologist") {
get {
parameters('active.as[Boolean].?, 'skip.as[Int].?, 'limit.as[Int].?).as(GetAllOncologists) {
req =>
complete {
(oncologistActor ? req).mapTo[OncologistList]
}
}
}
}
While this is returning without issue. I'd like to move some of the properties of OncologistList into the response header rather than returning them in the body. Namely, I'm returning total record counts and offset and I would like to generate a previous and next URL header value for use by the client. I'm at a loss on how to proceed.
I think you can use the onComplete and respondWithHeaders directives to accomplish what you want. The onComplete directive works with the result of a Future which is exactly what ask (?) will return. Here is an example using a case class like so:
case class Foo(id:Int, name:String)
And a simple route showing onComplete like so:
get{
parameters('active.as[Boolean].?, 'skip.as[Int].?, 'limit.as[Int].?).as(GetAllOncologists) { req =>
val fut = (oncologistActor ? req).mapTo[Foo]
onComplete(fut){
case util.Success(f) =>
val headers = List(
RawHeader("X-MyObject-Id", f.id.toString),
RawHeader("X-MyObject-Name", f.name)
)
respondWithHeaders(headers){
complete(StatusCodes.OK)
}
case util.Failure(ex) =>
complete(StatusCodes.InternalServerError )
}
}
}
So if we get a successful result from the ask on oncologistActor we can then leverage the respondWithHeaders to add some custom headers to the response. Hopefully this is what you were looking for.
So i have asked about this before and have changed a lot of code around.
Spray Routing Doesn't match anything
Now I am executing my functions that return HTTPresponses insided detach() blocks so that i dont block. These then are completed and return to the client, but I still can't seem to get my routing to work.
In my tests, a request to a single slash works fine, but anything else, such as this create user path shown below fails. I can't seem to figure out why, and spray routing uses so many constructs I'm having a hard time figuring out how the system works well enough to find out whats happening.
I tried inserting logRequest blocks around certain paths thinking that might show me whats happening, but none of them seem to get hit. Any help would be greatly appreciated.
val route: Route = {
host("fakebook.com", "www.fakebook.com") {
pathSingleSlash {
complete("pong")
} ~
pathPrefix("users") { req =>
path("newuser") {
put {
detach() {
complete(genericPut(CreateUser(req.request)))
}
}
} ~
... rest of routing
And here is what my scalatests look like, the simple Put passes, but the put with newuser doesn't
val createUserSuccessRequest = Put(Uri("http://www.fakebook.com/users/newuser") withQuery(F_User.lastNameString -> "Doe", F_User.firstNameString -> "John", F_User.bioString -> "i like foobar",
F_User.ageString -> "42", F_User.dobString -> dateFormatter.format(new Date(1970 - 1900, 5, 7))))
"The FakeBook route" when {
"When sending a single slash request" should {
"respond with a simple pong" in {
Get() ~> logRequestResponse("plain get final request and response")(sealRoute(route)) ~> check {
assert(responseAs[String] == "pong")
}
}
}
"Running route to create a user with PUT" should {
"forward a message to the backbone to create a new user" in {
createUserSuccessRequest ~> logRequest("create user final request and response"){sealRoute(route)} ~> check {
expectMsg(CreateUser(createUserSuccessRequest))
}
}
}
}
For anyone else trying to solve this issue:
a lot of these directives actually DONT extract anything, so having the lambda inputs i have like req => and req2 => will not work.
It turns out, spray routing is designed so that you never have to touch the RequestContext as I have done with my functions (which is why I try to access it). They extract only the useful data. Rather than do things as I should and change my function signatures, i am going to (for now) do a hotfix that has worked.
if you absolutely must have the requestcontext, so long as you don't break it somehow, you can extract it by making your own extraction directive like so
val extractRequestContext = extract(x => x) and wrap your code in that
I did this
path("somepath") {
detach() {
extractRequestContext { request => complete(someFunc(request)) }
}
}
In the future I should learn to use the DSL more correctly and extract what I need from the request context using directives and pass THOSE to the functions
I am currently using Gatling and I have a scenario whereby I perform a number of GET requests and depending on the body of the responses I would like to perform a different scenario.
I have this at the moment that doesn't appear to work as expected -
val repeatSpin = scenario("repeatScenario1").repeat(10) {
exec(
scenario1
)
.doIf(bodyString => bodyString.equals("<SwitchEvent/>")){
exec(scenario2)
}
}
What am I doing wrong?
It looks like you've got the .doIf parameters wrong - it either takes a key in the session and the value you expect, like:
.doIf("${bodyString}", "<SwitchEvent/>") { myChain }
Or, an Expression[Boolean] - the argument you get is the session; to get values out of a session you do something like session("bodyString").as[String]. So, passing a function to the doIf could look like
.doIf(session => session("bodyString").as[String].equals("<SwitchEvent/>")) { myChain }
This is what the section of code looks like
get{
respondWithMediaType(MediaTypes.`application/json`){
entity(as[HttpRequest]){
obj => complete{
println(obj)
"ok"
}
}
}
}~
I can map the request to a spray.http.HttpRequest object and I can extract the uri from this object but I imagine there is an easier way to parse out the parameters in a get request than doing it manually.
For example if my get request is
http://localhost:8080/url?id=23434&age=24
I want to be able to get id and age out of this request
Actually you can do this much much better. In routing there are two directives: parameter and parameters, I guess the difference is clear, you can also use some modifiers: ! and ?. In case of !, it means that this parameter must be provided or the request is going to be rejected and ? returns an option, so you can provide a default parameter in this case. Example:
val route: Route = {
(path("search") & get) {
parameter("q"!) { query =>
....
}
}
}
val route: Route = {
(path("search") & get) {
parameters("q"!, "filter" ? "all") { (query, filter) =>
...
}
}
}