Scalatra: printing the HTTP status code of all APIs - scala

I have a scalatra servlet with multiple REST APIs. For logging purposes, I use the after() method to print out the return status code after each API is called.
after() {
logger.info("request {} {} returned with status code {}", request.getMethod, request.getRequestURL, response.getStatus.toString)
}
I have noticed that when a method returns with halt, the status code is printed correctly, but when the method return a status code in the last line (without halt), the status code which will be printed will always be 200, regardless of real status returned.
For example:
post("/users/:user") {
try {
//some logic here...
if(condition)
halt(InternalServerError("DB error")) //this will cause status 500 to be printed in the 'after' method
} catch {
case e: Exception =>
InternalServerError("an unknown error occurred") //this will cause status 200 to be printed in the 'after' method
}
}
The user gets back the real status code (500) in both cases.
Any idea why this happens? Is this a bug?
I posted this question on the scalatra-user mailing list, but the list seems to be quite inactive.
Aliza

(disclaimer: I'm not a Scalatra developer but I have been using it for a project. This is based on me reading the code some time ago.)
This has to do with the way Scalatra is handling thrown exceptions (the relevant code seems to be start from this one). If the somewhere in runActions an exception is thrown (halt throws a HaltException), the catch block of cradleHalt will be called and we go to renderHaltException which will set the response status code.
It's not exactly the same when you're not calling halt but returns an ActionResult directly. In that case, executeRoutes seems to produce a value which is then passed on to renderResponse, which will then call renderResponseBody and finally the renderPipeline. This block seems to be the place where the actual status code from an ActionResult is actually set. However, the after function is already called (it was called in actionResult, before executeRoutes returns). So what you get is exactly your behavior: when you don't use halt, the correct response status is only set in the actual response but not your logging call.
You've probably tried this, but the quick fix to your InternalServerError not generating the correct HTTP status code when logged is to simply wrap it in another halt call.
As to whether this is a bug, I can't say. I'm guessing probably not, because they did say in the executeRoutes documentation that after is called before the actionResult is passed to renderResponse. What was not clear was that the act of rendering actionResult also sets the HTTP error code, which you wanted to log before.
You'll have to confirm with them on that :).

Related

Why does my API call not return when everything is synchronous?

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.

Empty response on long running query SailsJS

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.

cancel request when its already finished

consider the following (pseudo) code:
let request = Alamofire.request(...) {
//handler called when requests has been completed
//do some processing here
}
//some processing here, could take a while
request.cancel()
Question:
what happens if the request has already been fully completed (and the handler called) when the request.cancel() is done?
Will this return an error?
Is the handler called again?
Or (what I am hoping for) nothing...?
If the handler called that means request has its answer that can mean two things: Either request is succesfull, you have what you asked or request is not succesfull which means you will get an error.
Either way if you got your response request.cancel() will mean nothing.

Execution stops on Cookies.getCookie() call with no exceptions

I'm trying to read a Cookie value in my server side implementation class. After some debugging my code now looks like this:
logger.info("Initiating login");
String oracleDad;
try {
logger.info("inside try");
oracleDad = Cookies.getCookie("dad");
logger.info("Read dad from cookie: " + oracleDad);
} catch (Exception e) {
logger.error("Failed to read dad from cookie", e);
oracleDad = "gtmd";
}
When I execute this code my onFailure block is fired with a Status code Exception:
com.google.gwt.user.client.rpc.StatusCodeException: 500 The call
failed on the server; see server log for details
My logging output on the server looks like this:
[INFO] c.g.e.server.rpc.MyProjectImpl - Initiating login
[INFO] c.g.e.server.rpc.MyProjectImpl - inside try
How is it possible that neither logger, the INFO or the ERROR, fire after the Cookies.getCookie() call? I'd hoped that by adding the catch(Exception e) I'd get some error message explaining why the code fails. But execution just seems to stop silently.
I'm using com.google.gwt.user.client.Cookies. I thought client code can be run on the server, just not vice versa. Is that correct? Is there something else I'm missing?
I'm using com.google.gwt.user.client.Cookies. I thought client code can be run on the server, just not vice versa. Is that correct? Is there something else I'm missing?
No, that's not correct, yes there is something you are missing: Server code can't run on the client, and client code can't run on the server.
You are not getting an Exception. You are getting an Error or some other Throwable.
Try catching Throwable in your try/catch block, and you'll see that you are getting an error where the server JVM is unable to load the Cookies class, because something is wrong. The JVM thinks that a native library is missing (because it doesn't know what JSNI is or how to run it), so it throws an UnsatisfiedLinkError:
Thrown if the Java Virtual Machine cannot find an appropriate native-language definition of a method declared native.
In GWT, the Cookies class is meant to interact with the browser itself to see what cookies have been defined on the currently loaded page. To use cookies on a J2EE server, ask the HttpServletRequest object for the cookies it knows about, and their values.

GWT: What to expect when an HTTP request fails

I'm sending an HTTP request with RequestBuilder.send(). I would expect to see successful responses come back in onResponseReceived(), and errors come back in onError(). But that's not what I'm seeing. GWT calls onResponseReceived() regardless of success or failure.
Does anyone know what I should really expect? Do you have any information that would help me detect and report errors better?
Here's some sample code:
builder.sendRequest(null, new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
Header[] debugHeaders = response.getHeaders();
String debugHeaders1 = response.getHeadersAsString();
int debugStatusCode = response.getStatusCode();
String debugStatusText = response.getStatusText();
String debugText = response.getText();
handleResponse(response);
}
#Override
public void onError(Request request, Throwable exception) {
reportError();
}
});
I can force an error on my computer by disabling the Wi-Fi. In that case onResponse() is called. getHeaders() returns an array of length 1, with the only entry set to null. getHeadersAsString returns "". getStatusCode returns 0. getStatusText() returns "". getText() returns "".
If this is always the case, I can look for that in my code. However, I expect that different browsers, different errors, etc, will cause a different result. Is there any good way to always detect an error?
(As long as there are no HTTP problems, my code works fine.)
This the expected behavior; see comments in: https://code.google.com/p/google-web-toolkit/issues/detail?id=2858
According to documentation onResponseReceived is called in both cases (success or not).
I got the same on old browsers when my browser tried to go to download something and the prev. http request was not completed. So maybe try to wait untill the response is completed, maybe try to add some 200 msec. delay somewhere.
In my application i ignore when status code is 0.