I'm looking for a way to write a catch all route in actix web. What naturally makes sense to me would be to have a "/*" route, but this gives me an error about tail segments.
async fn not_found() -> impl Responder {
"404"
}
HttpServer::new(|| {
App::new()
.service(Files::new("/", "./").index_file("index.html")
.route("/*", web::get().to(not_found))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
actix_router::resource Tail segments must have names. Consider .../{tail}*. This may become a panic in the future.
I have looked at the actix-web docs and can't find anything. I tried several things including web::scope and empty routes, but as actix web says, if the route isn't found, it responds with a 404 code.
Related
I have a Vue.js application on the frontend and a Node.js REST API on the backend (on AWS).
I was making a call to the backend from the front end and getting a 504 error (gateway timeout) and I couldn't figure out why. Then I copied the code from an almost identical pattern elsewhere in the application and it worked. I don't know why it worked. I'm hoping that someone can explain to me why my code didn't work and why the code I copied does work.
First you need to know what my code looks like. So here it is:
FRONTEND:
async saveTimestamp() {
try {
await api.post(`blahBlahEndpoint`);
this.flag = false;
} catch (err) {
console.log('err=', err);
}
}
BACKEND:
RequestHandler:
require('./blahBlahController').setSomething(params);
return new HttpResponse(204);
Controller:
setSomething(params) {
this.dao.setSomething(params);
}
DAO:
setSomething(params) {
this.db.runQuery('blah blah query with params');
}
So essentially, the front end sends a post request to the backend. The endpoint is our RequestHandler, and once it figures out the right controller and method for the job, it requires the controller and calls the method, passing it any parameters that came with the request. The controller in turn calls a method on the DAO passing the parameters along. The method on the DAO (setSomething(...)) injects the parameters into a query string which gets passed to db.runQuery(...). db.runQuery is from a third party library. It essentially runs the query against our database (in my case, doing a simple field update). It's asynchronous and therefore returns a promise. As you can see, I'm ignoring the promise and just returning right away (I don't need the results of the query so I just let it do its thing and carry on). So other than runQuery(...), everything is synchronous. Why then does my frontend call timeout? After about a minute of waiting, the catch block catches a 504 error, which google tells me is a timeout error.
Now let's look at the fix:
FRONTEND:
async saveTimestamp() {
try {
await api.post(`blahBlahEndpoint`);
this.flag = false;
} catch (err) {
console.log('err=', err);
}
}
BACKEND:
RequestHandler:
await require('./blahBlahController').setSomething(params);
return new HttpResponse(204);
Controller:
async setSomething(params) {
await this.dao.setSomething(params);
}
DAO:
async setSomething(params) {
await this.db.runQuery('blah blah query with params');
}
Very simple. I just converted everything on the backend to async/await. But why does this fix the problem. I'm obviously not understanding how the async/await pattern handles promises and/or how that changes the dynamic between frontend and backend (at least with REST APIs).
Here's some assumptions I'm making that may be false:
1) Without the async/await pattern (where the bug existed), all method calls from the RequestHandler to the DAO only return when the method is done, and they return nothing (with the exception of the RequestHandler which returns a HttpResponse with a 204 status to the front end).
2) Because runQuery is asynchronous, it returns a promise, and it returns it immediately (i.e. before it is done).
3) Since DAO.setSomething(...) does not await the call to runQuery, it carries on as soon as it's done calling runQuery. The promise returned from runQuery is dropped. While runQuery does its thing (updating the DB), the API begins the return portion of its round trip, returning nothing.
4) RequestHandler finally returns an HttpResponse object to the frontend. At the frontend, this object becomes the object of the Promise being awaited, effectively resolving the promise and permitting the API call to stop awaiting.
^ 4) is what doesn't seem to happen. All backend logs indicate that everything works just as I describe above, right up to the point of returning the HttpResponse, and the updates to the database corroborate this. This tells me there is an issue with how my code above (pre-fix) effects the way by which the backend returns to the frontend, but what exactly?
Here's what I imagine is happening with the fix:
1) No changes to frontend. Frontend makes call to backend like before.
2) In RequestHandler, call to setSomething(...) in controller is made asynchronously, which means it returns a promise right away and is awaited.
3) In Controller's setSomething(...), call to DAO.setSomething(...) is made. Since this is asynchronous, it returns a promise right away and is awaited.
4) In DAO.setSomething(...), call to runQuery(...) is made. Since runQuery(...) is asynchronous, it returns a promise right away and is awaited.
5) Once the promise returned from runQuery(...) is resolved (because runQuery(...) finishes), DAO.setSomething(...) returns.
6) The promise returned from DAO.setSomething(...) is resolved by DAO.setSomething(...) returning, and it stops awaiting. This causes Controller.setSomething(...) to return.
7) The promise returned from Controller.setSomething(...) is resolved by Controller.setSomething(...) returning, and RequestHandler stops awaiting.
8) RequestHandler returns the HttpResponse object to the frontend with status 204.
9) The promise returned from the api call is resolved by way of the HttpResponse object being returned. The promise resolves into the HttpResponse object. The frontend stop awaiting and continues on.
^ Hopefully, this gives a detailed account of what my understanding is surrounding frontend requests and backend responses, and what happens when promises and async/await patterns are involved. Does anyone care to correct any misunderstandings I have? Anything that would explain why the frontend wasn't getting a response in the pre-fix state?
Thanks very much for any forthcoming responses.
I have an application with following back-end-technologies: scala/slick in playframework. Front- and back-end communicate via REST.
Now what I want to do is simply return a created/inserted (updated) row back to the front-end of my application. I thought about doing something like this:
def createClient = Action.async { implicit request =>
request.body.asJson.map(_.validate[ClientModel]) match {
case c: JsSuccess[ClientModel] =>
clientDTO.createClient(c.get).map{
cnt => Ok(Json.obj("id" -> cnt.id))
}.recover {
case e: Exception => BadRequest("Could ssnotsdreate client")
}
}
}
My code compiles but it gives me this error message while running:
XMLHttpRequest cannot load http://localhost:9002/clients. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 500.
I read about adding CORS to my application but would prefer to solve it otherwise. I thought there has to be a proper, elegant way to return a just created/inserted object back to the front-end, since it should be a core feature of any client-server communication.
I'm relatively new to scala, so please don't get hung up on my code and rather view it as pseudo code. This is a rather general question. Thank you!
I'm currently running SailsJS on a Raspberry Pi and all is working well however when I execute a sails.models.nameofmodel.count() when I attempt to respond with the result I end up getting a empty response.
getListCount: function(req,res)
{
var mainsource = req.param("source");
if(mainsource)
{
sails.models.gatherer.find({source: mainsource}).exec(
function(error, found)
{
if(error)
{
return res.serverError("Error in call");
}
else
{
sails.log("Number found "+found.length);
return res.ok({count: found.length});
}
}
);
}
else
{
return res.ok("Error in parameter");
}
},
I am able to see in the logs the number that was found (73689). However when responding I still get an empty response. I am using the default stock ok.js file, however I did stick in additional logging to try to debug and make sure it is going through the correct paths. I was able to confirm that the ok.js was going through this path
if (req.wantsJSON) {
return res.jsonx(data);
}
I also tried adding .populate() to the call before the .exec(), res.status(200) before I sent out a res.send() instead of res.ok(). I've also updated Sails to 11.5 and still getting the same empty response. I've also used a sails.models.gatherer.count() call with the same result.
You can try to add some logging to the beginning of your method to capture the value of mainsource. I do not believe you need to use an explicit return for any response object calls.
If all looks normal there, try to eliminate the model's find method and just evaluate the request parameter and return a simple response:
getListCount: function(req, res) {
var mainsource = req.param("source");
sails.log("Value of mainsource:" + mainsource);
if (mainsource) {
res.send("Hello!");
} else {
res.badRequest("Sorry, missing source.");
}
}
If that does not work, then your model data may not actually be matching on the criteria that you are providing and the problem may lie there; in which case, your response would be null. You mentioned that you do see the resulting count of the query within the log statement. If the res.badRequest is also null, then you may have a problem with the version of express that is installed within sailsjs. You mention that you have 11.5 of sailsjs. I will assume you mean 0.11.5.
This is what is found in package.json of 0.11.5
"express": "^3.21.0",
Check for any possible bugs within the GitHub issues for sailsjs regarding express and response object handling and the above version of express.
It may be worthwhile to perform a clean install using the latest sailsjs version (0.12.0) and see if that fixes your issue.
Another issue may be in how you are handling the response. In this case .exec should execute the query immediately (i.e. a synchronous call) and return the response when complete. So there should be no asynchronous processing there.
If you can show the code that is consuming the response, that would be helpful. I am assuming that there is a view that is showing the response via AJAX or some kind of form POST that is being performed. If that is where you are seeing the null response, then perhaps the problem lies in the view layer rather than the controller/model.
If you are experiencing a true timeout error via HTTP even though your query returns with a result just in time, then you may need to consider using async processing with sailjs. Take a look at this post on using a Promise instead.
I have a ServletFilter (which happens to be a GuiceShiroFilter) that processes incoming web requests before they go to a Jersey 1.x Resource.
However, in some situations (namely when Shiro finds that the request is not authenticated), I want to change which Jersey resource answers my request, without the resource that otherwise would have answered even being able to respond.
Here's what I have (in my Shiro AuthenticatingFilter.onLoginFailure()):
ServletRequest request = ...;
RequestDispatcher disp = request.getRequestDispatcher("/resource/that/always/responsds/with/a/403");
try {
disp.forward(request, response);
} catch (ServletException | IOException e) {
e.printStackTrace();
}
// this is needed to prevent the woud-be resource from responding as well
return false;
The problem of this server-side redirect is that not returning false will invoke both my resource for "/resource/that/always/responsds/with/a/403" and the original would-be response, and in the best case the response body contains both responses concatenated.
Is there a way to modify an existing instance of (Http)ServletRequest from a Filter such that later on, only the redirected-to resource can answer?
I dug into the issue a little bit and I realized that there is little I can do here, given that I don't control how the filter chain is processed further.
In a generic Filter, the implementer can wrap the current ServletRequest (in doFilter() in a HttpServletRequestWrapper implementation. This wrapper can then override the request URI. However, this requires that the implementer has control over how the filter chain is continued (which is where the wrapped request can be fed back into the execution path), but this is not the case in my situation, where Shiro controls that.
With Shiro, the filter chain continuation is controlled in AdviceFilter.doFilterInternal(), many layers above my own AuthenticatingFilter implementation.
So for now, my best bet is to do what I already described above: Invoke another resource - free from filters - by using a RequestDispatcher and stop the filter chain by returning false at the end of my AuthenticatingFilter's onLoginFailure()
I'm trying to set up some tests for an API made by a coworker with spray.io, and I'm encountering some odd behavior. When a request results in an error for any reason, we want to return a JSON value along the lines of:
{"status":false,"message":"useful message here"}
This happens just fine in the actual browser. I have navigated to an unhandled route in the web browser, and I get the desired JSON value. So, I want to test this. Now, since I'm new to spray.io, I started off with the very simple test:
"leave GET requests to root path unhandled" in {
Get() ~> myRoute ~> check {
handled must beFalse
}
}
This went fine, no problems. Since it's my first time playing with spray.io, I looked at some of the sample tests for testing false routes, and wrapped myRoute with sealRoute() so I could check the response without failing tests:
"leave GET requests to root path unhandled" in {
Get() ~> sealRoute(myRoute) ~> check {
handled must beTrue
}
}
This also works fine. So, I decided to just make sure the text of the response was usable with this, before I went to the trouble of parsing JSON and verifying individual values:
"leave GET requests to root path unhandled" in {
Get() ~> sealRoute(myRoute) ~> check {
responseAs[String] contains "false"
}
}
This is failing. To investigate, I threw a simple line of code in to log the actual value of responseAs[String] to a file, and I got this:
The requested resource could not be found.
Can anyone tell me what I'm doing wrong? I'm thinking that one of the following is occurring:
responseAs[String] is doing more than taking the exact response and giving it back to me, applying some type of filter along the way
The framework itself is not fully evaluating the query, but rather making a mockup object for the test framework to evaluate, and therefore not executing the desired 'turn errors to json' methods that my co-worker has implemented
I have tried searching google and stack overflow specifically for similar issues, but I'm either not putting in the right queries, or most other people are content to have the default error messages and aren't trying to test them beyond checking handled must beFalse.
Edit - This is the relevant part of the RejectionHandler:
case MissingQueryParamRejection(paramName) :: _=>
respondWithMediaType(`application/json`) {
complete(BadRequest, toJson(Map("status" -> false, "message" -> s"Missing parameter $paramName, request denied")))
}
Okay, so with insight from here and a coworker, the problem has been found:
Basically, the custom RejectionHandler was defined within our custom Actor object, and it wasn't coming into scope in the tests. To resolve this, the following steps were taken:
Moved the definition for the custom RejectionHandler into its own object in a separate file (as it had to do its own imports, it was causing a "encountered unrecoverable cycle resolving import" error)
Imported this new object into both the original file and the test spec.
(fun fact - http://spray.io/documentation/1.2.2/spray-routing/key-concepts/rejections/#rejectionhandler seems to demonstrate the RejectionHandler as a top-level object but you can't have top-level implicit vals in Scala, hence the need for an object)