How can I solve issue of missing static content when serving html from Spray (or Akka-Http) ? Base url of my service is /api (even though it should be irrelevant in this case).
Here is my route
get {
pathPrefix("swagger") {
pathEndOrSingleSlash {
getFromResource("swagger-ui/index.html")
} ~
getFromResourceDirectory("swagger-ui")
}
}
Loaded html can find css and js file when I open it as
/api/swagger/
but when I open
/api/swagger (without trailing slash)
loaded html attempts to get content from
/api/css/reset.css instead of /api/swagger/css/reset.css
How should I rewrite my route to cover both cases ?
I ended up adding redirect. If anyone knows more elegant solution please post.
pathPrefix("swagger") {
CachingDirectives.cachingProhibited {
pathEnd {
redirect("/api/swagger/", StatusCodes.TemporaryRedirect)
} ~
pathSingleSlash {
getFromResource("swagger-ui/index.html")
} ~
getFromResourceDirectory("swagger-ui")
}
}
Use the redirectToTrailingSlashIfMissing directive instead.
ref: http://doc.akka.io/docs/akka/2.4/scala/http/routing-dsl/directives/path-directives/redirectToTrailingSlashIfMissing.html
Related
So basically, I'm writing some code that will make it possible for users to upload a file to a server. I've already succeeded in uploading a file via an HTML form (with MultiPart.FormData), but when I try 'curl -X POST -F file="filepath" localhost:8080/upload', I get a '404 not found' message.
I already read the documentation about Akka, but I just can't get to know why it works in one way and not in the other. What am I doing wrong here?
val route =
post {
path("upload") {
fileUpload("file") {
case (metadata, byteSource) =>
val sink = FileIO.toPath(Paths.get("path of the image") resolve metadata.fileName)
val writeResult = byteSource.runWith(sink)
onSuccess(writeResult) { _ =>
complete("file got uploaded")
}
}
}
} ~
complete("404 not found")
You can see in path directive source that it accepts a path prefix with the path end. So if you use path("upload") it will accept only paths ended with /upload/ but won't accept paths ended with /upload (without path end symbol /).
If you wont to use both /upload/ and /upload paths, you should use
pathPrefix("upload") ~ pathEndOrSingleSlash
Also, you can use ignoreTrailingSlash directive.
public function actionDone($id)
{
if ($model = $this->findModel($id)) {
$model["status"] = 3;
if ($model->save()) {
return $this->redirect(['test/index']);
}
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
It works only for the first time for each link. After that its just redirects to the 'test/index' without doing anything. Seems like browser (or smth else) remember, that if we open, for example, page site.com/?r=test/done&id=2 it should redirect to 'test/index' anyway.
Why is that? How can I fix it?
I even tried put die(); in the beginning of the method - anyway it redirects to 'test/index' until I use different link with another ID.
Thanks!
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...
When I create an object through POST in my Spray app I'd like to return a 201 status, together with a Location header that has the absolute URI of the newly created resource (including host port & contextRoot of my app)
Here is an example code fragment from my application...
post {
respondWithHeaders(Location( fullyQualifiedUri("/movies"))) {
entity(as[MovieImpl]) { (movieToInsert: MovieImpl) => {
addMovies(movieToInsert)
complete("OK")
}
}
}
}
Note that I now have to write the method 'fullyQualifiedUri' to return
a URI with host, port, etc. It would be nice if Spray did that for me
automagically.
Side note:
I think that having the Location header include the absolute URI of the newly
created resource makes it easier on my REST API's clients (although it does seem that there are a variety of opinions on this.)
Thanks in advance for any guidance you can provide.
-chris
To build the URI you'll need the Id of the newly created resource. Then you can use the requestInstance directive to get the incoming request URI and build the new resource URI from it. You also need to set the return code to Created to meet your requirements:
post {
requestInstance { request =>
val movieId = ???
respondWithHeaders(Location( request.uri.withPath(request.uri.path / movieId))) {
entity(as[MovieImpl]) { (movieToInsert: MovieImpl) => {
addMovies(movieToInsert)
complete(StatusCodes.Created)
}
}
}
}
}
Hi I have this code in my gwt app which purpose is to chage to URL as follows:
public void goToSignUpPage(boolean isDeployed) {
String url = (isDeployed == true ? "signup.html" : "signup.html?gwt.codesvr=127.0.0.1:9997");
Window.Location.replace(url);
However what happens it redirects into this URL:
http://127.0.0.1:8888/mygwtapp/signup.html?gwt.codesvr=127.0.0.1:9997
Where the working URL is this:
http://127.0.0.1:8888/signup.html?gwt.codesvr=127.0.0.1:9997
BTW, mygwtapp is the gwt module named defined in MyGwtApp.gwt.xml
<module rename-to='mygwtapp'>
Any ideas why the URL is appended by the gwt module name? Any way to fix this?
All you needed was to add in GWT.getHostPageBaseURL() to get the full URL for your web application without it appending to the module name.
Try this out:
public void goToSignUpPage() {
String url = GWT.getHostPageBaseURL() + "signup.html";
if(!GWT.isProdMode()) {
Window.alert("We are in development mode!");
url += "?gwt.codesvr=127.0.0.1:9997";
}
Window.Location.replace(url);
}
I've also removed your parameter "isDeployed" and replaced it with GWT.isProdMode() within the method to check if you're in production or development mode.
With a paramater:
public void goToSignUpPage(Boolean isDeployed) {
String url = GWT.getHostPageBaseURL() + "signup.html";
if(!isDeployed) {
url += "?gwt.codesvr=127.0.0.1:9997";
}
Window.Location.replace(url);
}
Hope this helps!