How to use HttpResponseFeature in a ASP.net Core Middleware - asp.net-core

I just found out that "RequestFeatures" is a thing in ASP.net Core, news to me..
Anyways..
I have created a middleware that needs to alter the response body and the headers provided with the response.
Im used to do this by alterntiv it directly on the HttpContext context-object provided as an argument to the Invoke-method.. however I read somewhere that context.Features is the way to go due to optimizations and what not.. (is this true?, I get that its alot easiter to test the Features, than to "mock" an entire HttpContext which has been historically a painful thing to do..) So I created my own implementation of the HttpResponseFeature and registred it in my Invoke method using:
httpContext.Features.Set<IHttpResponseFeature>
(
//Registering my own HttpResponseFeature that takes an argument..
new MyHttpResponseFeature(httpResponseMessage)
);
However, the OnCompleted or the OnStarting-methods never runs. I have added a few breakpoints to validate this, but the breakpoints are never hit. Am I missing something?

Turns out that the FeatureCollection/RequestFeatures is a "new" thing if your building your very own custom HTTP server ontop of ASP.net Core.
An article that covers this fairly well is this one:
https://reynders.co/use-iserver-from-aspnet-core-to-create-your-own-web-server/

Related

Prevent ASP.NET Core application from using appsettings.json file

I would like to tell to ASP.NET Core application that even if appsettings.json file is there - ignore it.
I would prefer to write this as a comment but I'm still a newby here so I cannot ask questions.
I would like to understand what is the specific problem you are facing right now.
In general the usage or not of the appsettings file depends on your application.
For example, if you create a Web API using default .NET template, you can see that the appsettings file only has some configuration for logging, which you can even delete and nothing happens. You can run the application anyway and it works.
So, coming back to your question, it dependes on what your application is doing. If you have a specific library that needs to read configuration from this file, then you'll need to research how to change that default value.
If you are reading from that file, then you could set value in code instead. (this is obvious but since you didn't provide any more context I don't know what you are struggling with)

Serilog using EnrichDiagnosticContext with additional properties not being logged in SignalR Hub

I have recently implemented Serilog logging into my ASP.NET Core/.NET5 web app that uses SignalR. I'm using the Elasticsearch sink and everything is largely working as expected. I decided to add some additional HttpContext properties to be logged on each request, so I went down the road of extending the call to UseSerilogRequestLogging() in StartUp.cs as to enrich the diagnostic context with a couple of extra properties (mainly because this seemed like the simplest way to do it):
app.UseSerilogRequestLogging(options =>
{
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("HttpRequestClientIP", httpContext.Connection.RemoteIpAddress);
diagnosticContext.Set("UserName", httpContext.User?.Identity?.Name == null ? "(anonymous)" : httpContext.User.Identity.Name);
};
});
At first, this seemed to work as expected until I noticed it wasn't always working. I really want the extra properties logged on all log records written, and it seems to work fine on log records that are written automatically by Serilog when typical HTTP GETs, HTTP POSTs, etc. occur... But in my Signalr Hub class, I have a couple of places where I'm manually writing my own log records like Logger.Log(LogLevel.Information, "whatever.."), but these extra properties are simply not there on these records.
What am I missing here? Is it something about this being in a Signalr Hub that makes them unavailable? Or perhaps there's something I'm doing wrong with my Logger.Log() calls?
Any ideas would be appreciated.
Thanks-
It's not gonna to work with signalR.
Behind the screen, app.UseSerilogRequestLogging make use of a middleware in the request pipeline, that call RequestLoggingMiddleware as what you can see in detail here.
SignalR use the first Http request to setting to connection up to websocket, which won't goes through the pipeline at all. Therefore, doesn't have anything to do with RequestLoggingMiddleware, which you are using to logging out the request.
I finally ended up going with a couple of custom Enrichers. I did experiment briefly with middleware vs enrichers and they both seem to work as expected. Both always added the additional properties to all log entries. I'm still not quite sure I understand why the DiagnosticContext option behaves the way it does, unless it is simply due to the logging in question being in a SignalR hub as #Gordon Khanh Ng. posted. If that were the root of the problem though, you wouldn't think the enrichers or middleware would work either.

Change Key of HttpContext.Request.Query item in ASP.NET Core

I am trying to work around a issue with a 3rd party filter. My current plan is to put a filter in front of that filter to "fix" the query string so it does not error out.
I made an ActionFilterAttribute and added it into the filter list. It is running fine. I am adding my logic in the OnActionExecuting method.
The first item of context.HttpContext.Request.Query has a Key that is a json structure. I need to change that Key to be {}.
Problem is that both context.HttpContext.Request.Query and context.HttpContext.Request.QueryString are read-only.
How can I alter the context.HttpContext.Request.Query or the context.HttpContext.Request.QueryString?
EDIT - The Underlying Problem:
BreezeJS did a minimal level upgrade to support .NET Core. In this upgrade, part of the code expects that every call that has any parameters to return an IQueryable (QueryFns.cs Line 32). From reading the code it seems like this is an error (the calling function (the actual filter) seems to just expect null to be returned not an Exception.)
Either way, this makes moving to .NET Core very hard.
I considered my other options and if this fails, I will continue to pursue them:
Submit a pull request to fix the issue: The project has not accepted any pull requests in over a year and a half. So it seems unlikely my request will be taken.
Fork my own branch: I would rather not have to create and maintain a separate version with my own build and publishing pipeline.
Find a way to make the Breeze filter ignore the call when the result is not an IQueryable: I am currently looking into this one. (This question.)
Find a way to send my call from the client differently so that breeze ignores calls that do not return IQueryable: The return type of the call is owned by the service. And this is an issue with the service. I would rather not have to have tight coupling between the service and the client such that the client is crafting workarounds for service filter issues.

Autofac request lifetime scope always null using Web Api 2.2 integration (3.4)

I'm using the autofac web api 2.2 integration (version 3.4) and I'm debugging one of my controllers. It seems like the request lifetime scope is always null when I try the following from any point where I'd expect there to be an active request:
var x = (AutofacWebApiDependencyResolver) GlobalConfiguration.Configuration.DependencyResolver;
x.GetRequestLifetimeScope(); // always null
Is this expected behavior? I'm a bit confused because it looks like all of my components are resolved at 'root' which, according to the documentation is bad. I was expecting it to automatically put it into a request scope when I register using InstancePerRequest(). It seems like:
AutofacWebApiDependencyResolver.BeginScope()
never happens.
I could post more sample code, but I've reproduced it following the quick start guide with very basic controllers, so I suspect it's more of a flaw in my logic above and was hoping someone could point it out. Thanks!
I had the same problem and my issue was that I copied my autofac configuration from a MVC project and was registering my dependencies with InstancePerRequest. When I changed that to InstancePerDependency instead it solved the problem and GetRequestLifetimeScope was working.
builder.RegisterType<X>().As<IX>().InstancePerDependency();
In action method, you can get the request scope by
var scope = Request.GetDependencyScope();

MVC4- An asynchronous operation cannot be started at this time --- code works fine on MVC3

I converted a project from MVC3 to MVC4 and also from Entity Framework 5 to EF 6.1. In the code there is a VBHTML page that has a code section "Using Html.BeginForm.... end using". in this page, a user can select a file and click submit. That calls a POST method in a controller. The method in the controller uploads the selected file to Google drive using resumable uploader (aynchronous) of GData API. ResumableUploader has to be used (GData API restrictions for uploading PDF and big files). Now, this code has always worked in the past in MVC3. As soon as the platform was changed to MVC4, started getting this exception:
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>
at System.Web.AspNetSynchronizationContext.OperationStarted() at Google.GData.Client.ResumableUpload.ResumableUploader.AsyncStarter
(AsyncResumableUploadData data, WorkerResumableUploadHandler workerDelegate,
Object userData)
I did put #Page Async="true" in the VBHTML page, but that did not help. I am 100% sure this is related to MVC4 and/or .Net 4.5 because no other change has been done in the code (other than moving to MVC4 and moving to .Net4.5). I have two code branches now, one on MVC3 and one on MVC4. When I compile MVC3 and copy the output DLL in the app, the above issue does not surface. When I replace that DLL by MVC4 version, the above issue comes up. How to handle this?
This is the code that causes this issue:
Dim auth As Authenticator = New AuthSubAuthenticator("MyToken", authFactory.Token)
Dim uploader As New ResumableUploader(10485760)
AddHandler uploader.AsyncOperationCompleted, AddressOf UploaderCompleted
uploader.InsertAsync(auth, file, New Object())
The trace shows that the exception was thrown from the last line (uploader.InsertAsync)
I understand there is a await method that I could use. Did not try, instead, changed uploader.InsertAsync to uploader.Insert and the code works ok. But but the user has to wait until the upload is complete (for bigger files, for quite a while).
I am 100% sure this is related to MVC4 and/or .Net 4.5
Yes, this is a change in ASP.NET 4.5. However, it's important to note that the code was technically wrong before. ASP.NET 4.5 adds several safeguards to catch improper asynchronous usage. So, the MVC3 code was actually improper; it just wasn't getting caught and reported as such.
changed uploader.InsertAsync to uploader.Insert and the code works ok. But but the user has to wait until the upload is complete (for bigger files, for quite a while).
This is the "proper" way to do it on ASP.NET (on a side note, using await would be more efficient than Insert). ASP.NET is not designed to do work without a user connection.
Consider, for example, what would happen if the upload errors out; there's no way to notify the user that the upload in fact did not complete. For that matter, there's no way to notify the user that the upload did complete. Also, ASP.NET may recycle your application, which can kill an in-progress upload. For these reasons, doing "fire and forget" work is not recommended on ASP.NET.
However, if you're willing to live with those limitations, I describe on my blog a variety of ways to do fire and forget on ASP.NET. Note that ASP.NET 4.5.2 added a built-in way to do this: HostingEnvironment.QueueBackgroundWorkItem.