Express middleware to conditionally change response result - express

What I want to achieve is when an express handler fails either by throwing an unhandled exception or returning an empty response like undefined or [], I want the handler to return a predefined mock response rather than failing. This means my server never fails as it either returns the normal real data or predefined mock data.
Of course I will only turn this on in development environment and never in production.
I think a middleware is ideal because I don't want to pollute every handler logic by injecting the response check.
Is this possible with a middleware in express?
If not, what's a cleaner way of achieving this?

The return value of a middleware handler is irrelevant, because a middleware handler is asynchronous by nature. It does one of three things:
It completes the response by calling res.end(...) or similar.
It reports an error by calling next(err).
It delegates the decision to the next middleware by calling next().
Errors can be caught with an additional error-handling middleware, and exceptions can be converted into errors as discussed in [ExpressJs]: Custom Error handler do not catch exceptions.
However, you cannot change a response after it has been sent. Moreover, you write
an express handler fails ... by ... returning an empty response
but an empty response is not a failure. If you want to treat empty responses as failures, but only in production, I suggest that you handle them as special errors. Instead of responding with res.json([]), say, you write next({emptyResponse: []}) and have special error-handling middleware in development only to handle these:
app.use(function(err, req, res, next) {
if (err.emptyResponse) {
console.error(err.emptyResponse);
res.end("Mock response");
} else next(err); // delegate to the standard error handler
});
Perhaps there is a misconception what a response is. The server streams the response to the client, only the client can "get" the response in this sense. Responses cannot be passed between middlewares.

Related

HttpContext.Session in Blazor Server Application

I am trying to use HttpContext.Session in my ASP.NET Core Blazor Server application (as described in this MS Doc, I mean: all correctly set up in startup)
Here is the code part when I try to set a value:
var session = _contextAccessor.HttpContext?.Session;
if (session != null && session.IsAvailable)
{
session.Set(key, data);
await session.CommitAsync();
}
When this code called in Razor component's OnAfterRenderAsync the session.Set throws following exception:
The session cannot be established after the response has started.
I (probably) understand the message, but this renders the Session infrastructure pretty unusable: the application needs to access its state in every phase of the execution...
Question
Should I forget completely the DistributedSession infrastructure, and go for Cookies, or Browser SessionStorage? ...or is there a workaround here still utilizing HttpContext.Session? I would not want to just drop the distributed session infra for a way lower level implementation...
(just for the record: Browser's Session Storage is NOT across tabs, which is a pain)
Blazor is fundamentally incompatible with the concept of traditional server-side sessions, especially in the client-side or WebAssembly hosting model where there is no server-side to begin with. Even in the "server-side" hosting model, though, communication with the server is over websockets. There's only one initial request. Server-side sessions require a cookie which must be sent to the client when the session is established, which means the only point you could do that is on the first load. Afterwards, there's no further requests, and thus no opportunity to establish a session.
The docs give guidance on how to maintain state in a Blazor app. For the closest thing to traditional server-side sessions, you're looking at using the browser's sessionStorage.
Note: I know this answer is a little old, but I use sessions with WebSockets just fine, and I wanted to share my findings.
Answer
I think this Session.Set() error that you're describing is a bug, since Session.Get() works just fine even after the response has started, but Session.Set() doesn't. Regardless, the workaround (or "hack" if you will) includes making a throwaway call to Session.Set() to "prime" the session for future writing. Just find a line of code in your application where you KNOW the response hasn't sent, and insert a throwaway call to Session.Set() there. Then you will be able to make subsequent calls to Session.Set() with no error, including ones after the response has started, inside your OnInitializedAsync() method. You can check if the response is started by checking the property HttpContext.Response.HasStarted.
Try adding this app.Use() snippet into your Startup.cs Configure() method. Try to ensure the line is placed somewhere before app.UseRouting():
...
...
app.UseHttpsRedirection();
app.UseStaticFiles();
//begin Set() hack
app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
//this throwaway session variable will "prime" the Set() method
//to allow it to be called after the response has started
var TempKey = Guid.NewGuid().ToString(); //create a random key
Context.Session.Set(TempKey, Array.Empty<byte>()); //set the throwaway session variable
Context.Session.Remove(TempKey); //remove the throwaway session variable
await Next(); //continue on with the request
});
//end Set() hack
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
...
...
Background Info
The info I can share here is not Blazor specific, but will help you pinpoint what's happening in your setup, as I've come across the same error myself. The error occurs when BOTH of the following criteria are met simultaneously:
Criteria 1. A request is sent to the server with no session cookie, or the included session cookie is invalid/expired.
Criteria 2. The request in Criteria 1 makes a call to Session.Set() after the response has started. In other words, if the property HttpContext.Response.HasStarted is true, and Session.Set() is called, the exception will be thrown.
Important: If Criteria 1 is not met, then calling Session.Set() after the response has started will NOT cause the error.
That is why the error only seems to happen upon first load of a page--it's because often in first loads, there is no session cookie that the server can use (or the one that was provided is invalid or too old), and the server has to spin up a new session data store (I don't know why it has to spin up a new one for Set(), that's why I say I think this is a bug). If the server has to spin up a new session data store, it does so upon the first call to Session.Set(), and new session data stores cannot be spun up after the response has started. On the other hand, if the session cookie provided was a valid one, then no new data store needs to be spun up, and thus you can call Session.Set() anytime you want, including after the response has started.
What you need to do, is make a preliminary call to Session.Set() before the response gets started, so that the session data store gets spun up, and then your call to Session.Set() won't cause the error.
SessionStorege has more space than cookies.
Syncing (two ways!) the sessionStorage is impossible correctly
I think you are thinking that if it is on the browser, how can you access that in C#? Please see some examples. It actually read from the browser and transfers (use) on the server side.
sessionstorage and localstorage in blazor are encrypted. We do not need to do extra for encryption. The same applies for serialization.

log request for "CORS policy execution failed"

My ASP.NET Core 3.0 in a particular configuration/deployment logs:
[INF] CORS policy execution failed.
[INF] Request origin https://bla.com does not have permission to access the resource.
How can I log at that point the resource that was requested for debugging ?
(note this question is not about the actual issue or solving it)
(note that I am not after globally increasing the log level etc)
Well, that middleware is locked down pretty badly, and I haven't found any sensible way to hook into it.
If you want to replace the CorsMiddleware, you can't just create a decorator that calls Invoke() on the middleware, because you'll have no idea what happened.
Another solution might be to replace the CorsService:ICorsService registration in the service collection with a decorator, and then check the CorsResult after delegating the call to EvaluatePolicy(). That way you could emit an additional log message close to where the original message is emitted.
But there is another possible solution, both very simple and very crude: To check what happened in the request. Albeit that is a bit farther away from the original logged message.
The code below is a delegate added to the pipeline (in Startup/Configure, before .UseCors()) that checks if the request was a preflight request (the same way CorsService does), and if it was successful, i.e. the AccessControlAllowOrigin header is present. If it wasn't successful, it logs a message with the same EventId and source as the CorsService.
app.Use(async (ctx, next) =>
{
await next();
var wasPreflightRequest = HttpMethods.IsOptions(ctx.Request.Method)
&& ctx.Request.Headers.ContainsKey(CorsConstants.AccessControlRequestMethod);
var isCorsHeaderReturned = ctx.Response.Headers.ContainsKey(HeaderNames.AccessControlAllowOrigin);
if (wasPreflightRequest && !isCorsHeaderReturned)
{
ctx.RequestServices.GetRequiredService<ILoggerFactory>()
.CreateLogger<CorsService>()
.LogInformation(new EventId(5, "PolicyFailure"),
$"CORS preflight failed at resource: {ctx.Request.Path}.");
}
});
Based on my testing it seems to work. ¯\_(ツ)_/¯
It might not be what you were looking for, but who knows, maybe it will be useful for someone.
(Obviously a good way to deal with these things is to use a structured logging solution, like Serilog, and add enrichers to capture additional request information, or add stuff manually to a diagnostic context. But setting that up is quite a bit more involved.)

What is the advantage of using express error handler?

What is the advantage of using express error handler instead of handling the errors on the routes as they come as shown below. I am starting an open source project and I wanted to go with the best practices.
register: async function(req, res, next, error ){
User.findOne({username: req.body.username}, async function(err, __user){
try {
if(__user)
throw (
{
"type":"Registration Error",
"details" : `The username "${__user.username}" is already taken. Try another one.`
}
)
} catch(err) {
return res.status(500).json( {"error":{"type":err.type, "details":err.details}} )
}
Are there scenarios where it is better to use one way over the other?
To see the code above in its full context click here
From this article there are definately benefits of using express error handler.
For instance:
you can manage all your errors from one place
You write less code.
You can group errors and manage them depending on their types.
You can get more info from the article. :)
Using the built-in error handler in Express has a number of benefits:
It centralizes error handling in a single place, which makes it easier to maintain and update your error handling logic.
It allows you to handle errors in a consistent way throughout your application, rather than having to repeat error handling logic in multiple places.
It can help improve the readability and maintainability of your code by abstracting the error handling logic away from the main route handling logic.
In essence, instead of having try/catch and repeated return res.status(500).json() logic all throughout your routes, you handle errors in one place and only have to throw in said routes.

Possible to add an `app` method through middleware in Express?

I am trying to write a middleware that creates a new method on app created with express(). For example, I would like to achieve the following:
app.use(myMiddleware())
// ...
app.newMethod() // added through the previous middleware
Is there a way to achieve this? A way I have thought of, as referenced in some other questions, is to pass in app itself as a param to my middleware so that I could tweak it:
app.use(myMiddleware(app))
// ...
app.newMethod() // ok, definitely doable
However, this does not seem elegant enough.
Also, req.app and res.app references won't work for me in this case, since the (req, res, next) => {} function returned by myMiddleware() only executes when receiving requests, while I possibly want to access the method before even app.listen() is called.
Is there a way I can achieve this?
It really doesn't make sense to add an app method in middleware. The purpose of middleware is to process an incoming request, either in preparation for later middleware or later request handlers or to just handle the request itself in the middleware.
Middleware gets called over and over during incoming requests. It should never be used for something that should just happen once and it should only be used for processing related to an incoming request.
while I possibly want to access the method before even app.listen() is called
So, that definitely has nothing to do with an incoming request then so using middleware is just not the right design choice.
If all you're trying to do is to add your own method to the app object, you can do that when you are initializing your server:
const app = require('express')();
// add my own method to the app object
app.myMethod = function(myArg1, myArg2) {
// put the implementation here
}
app.use(...);
app.get(...);
app.get(...);
app.listen(...);
Then, anywhere you want, you can call app.myMethod(...).

Bypassing Play's HttpErrorHandler for 4xx errors

I'm writing a microservice in Play. I'd like my controller to be able to generate client errors (4xx) with a particular JSON response body. However, Play's default HttpErrorHandler kicks in, and replaces my response body with an HTML document.
How can I have my response returned to the client untouched?
I have looked into providing a custom HttpErrorHandler, but this doesn't give access to the response that my controller had generated; the signature is:
def onClientError(request: RequestHeader, statusCode: Int, message: String): Future[Result]
Edit: I can no longer reproduce this problem. Now, the error handler doesn't kick in -- which is the behaviour I'd expect. Most likely some form of user confusion / error.
A client error is a condition which is caused by the client, and Play doesn't know how to handle. That includes malformed headers, non-existing resources (read : No route available for that path).
In all cases, this won't hit a controller : It's handled before it's routed. That also means there is no body that can be passed along.
If it does hit a controller, you're free to return a Result with the proper response code and body. If it doesn't hit a controller, and the error handler is invoked, you need to return a response based on the request itself.
An example of what you're trying to achieve would be handy, since it's a bi t unclear to me.