Route ALL requests through OWIN Middleware - asp.net

I am having some troubles getting some very basic OWIN middleware to handle all requests to the IIS application. I am able to get the OWIN middleware to load per page request but I need it to handle requests for images, 404s, PDFs, and everything that could possible be typed into an address bar under a certain host name.
namespace HelloWorld
{
// Note: By default all requests go through this OWIN pipeline. Alternatively you can turn this off by adding an appSetting owin:AutomaticAppStartup with value “false”.
// With this turned off you can still have OWIN apps listening on specific routes by adding routes in global.asax file using MapOwinPath or MapOwinRoute extensions on RouteTable.Routes
public class Startup
{
// Invoked once at startup to configure your application.
public void Configuration(IAppBuilder app)
{
app.Map(new PathString("/*"),
(application) =>
{
app.Run(Invoke);
});
//app.Run(Invoke);
}
// Invoked once per request.
public Task Invoke(IOwinContext context)
{
context.Response.ContentType = "text/plain";
return context.Response.WriteAsync("Hello World");
}
}
}
Essentially, whether I request http://localhost/some_bogus_path_and_query.jpg or http://localhost/some_valid_request, all requests would get routed through the Invoke subroutine.
Is this possible to achieve with OWIN?
I have read threads like (How to intercept 404 using Owin middleware) but I am not having any luck. IIS Express keeps serving up 404 errors when I really need OWIN to just write Hello World in all cases, regardless of whether an asset is on disk or not.
Also, I have added runAllManagedModulesForAllRequests="true" to the web.config file and I still cannot get OWIN to fire when I am requesting images via a URL.

You have asked for couple of things altogether in your question. I'll try to answer them one by one. First you want to execute your Middleware for each request. This can be achieved by using StageMarkers within IIS integrated pipeline. All middleware executes after the last stage of StageMarker i.e. PreHandlerExecute. But you can specify when you want to execute your middleware. E.g. To get all incoming request in Middleware try mapping it before either MapHandler or PostResolveCache.
Second, You want to intercept the 404 error redirection. In the same thread that you mentioned; Javier Figueroa answered it in the sample code he provided.
Below is same sample taken from the thread you mentioned:
public async Task Invoke(IDictionary<string, object> arg)
{
await _innerMiddleware.Invoke(arg);
// route to root path if the status code is 404
// and need support angular html5mode
if ((int)arg["owin.ResponseStatusCode"] == 404 && _options.Html5Mode)
{
arg["owin.RequestPath"] = _options.EntryPath.Value;
await _innerMiddleware.Invoke(arg);
}
}
In Invoke method as you can see the response has been captured in pipeline which is already generated from IIS integrated pipeline. So the first option that you were thinking to capture is all the requests and then taking the next decision if it's 404 or not which might not work. So it's better if you capture 404 error like in the above sample and then perform your custom actions.

Just a note that you may also need 'runAllManagedModulesForAllRequests' set to true in your web.config:
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
</configuration>

Related

ASP.NET Core with SPA, how to handle invalid routes on root?

Our ASP.NET Core with Single Page App as client, is hosted on Azure Web Service. We noticed that all environments and deployment slots get an occasional POST action request on /index.html. In the ASP.NET Core application, http requests to the root is routed to the SPA application files through configuring the SPA static files provider middleware:
services.AddSpaStaticFiles(configuration => {
configuration.RootPath = "ClientApp/dist/ClientApp";
});
When these POST actions are requested on /index.html, the application will throw an exception:
The SPA default page middleware could not return the default page '/index.html' because it was not found, and no other middleware handled the request.
In turn, the exception causes issues in our performance monitoring as the exceptions are not caught/handled anywhere. Especially if this happens multiple times in a short time.
Question: What can we configure to either immediately return 403 or similar response, or setup such that we at least catch the exception?
The SPA default page middleware could not return the default page '/index.html' because it was not found, and no other middleware handled the request.
This problem arises when the wwwroot folder is not copied in build or publishing the project.
In either cases, both commands don't copy the wwwroot folder.
As a workaround, you can add this target to your project file:
<Target Name="AddGeneratedContentItems" BeforeTargets="AssignTargetPaths" DependsOnTargets="PrepareForPublish">
<ItemGroup>
<Content Include="wwwroot/**" CopyToPublishDirectory="PreserveNewest" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);#(Content)" />
</ItemGroup>
</Target>
What can we configure to either immediately return 403 or similar response, or setup such that we at least catch the exception?
Another reason can be , if your controller route attribute don't exactly match the request URL, then this type of error occurs.
Please refer the similar issue found in GitHub.
I have found a solution in an issue case on GitHub. This solution will call on the middleware only when the request meets the correct condition: when it's a GET request.
In the Configure method of the Startup class:
app.UseWhen(context => HttpMethods.IsGet(context.Request.Method), builder =>
{
builder.UseSpa(spa =>
{
// ... add any option you intend to use for the Spa middleware
});
});

Disable access to a Servlet

I'm developing a WebApp using JavaEE and I use a servlet to test some stuff.
Currently, my WebApp is set as when I go to my local url localhost:8080/myApp/test , I can run my test Servlet.
My plan is to deploy my project to a Web and I want to disable the Servlet, but not delete it. I mean, if in the future I visit my remote server via URL www.myNewWeb.com/test , I would like it throws an error od do nothing.
How could I do that?
There are many possible options here:
Option 1
Remove the mapping (annotation #WebServlet or url mapping entry in web.xml). In this case, any attempt to call this servlet will end with an error generated by the JEE container of your choice. It will try to map the servlet to URL, will obviously fail and throw an exception
The obvious drawback of this method is that you need to change the deployment configuration and if you'll want to run the same artifact in another envrironment where this servlet should work you won't be able to do so.
Option 2
Create some kind of configuration, load this configuration along with your application.
In the doGet (just for the sake of example) method do something like this:
public void doGet(request, response) {
if(config.isTestServletEnabled()) { // this is where the data gets read from configuration that I've talked about before
// do your regular processing here
}
else {
// this will happen when the servlet should not be activated
// throw an exception, return HTTP error code of your choice, etc
}
}
This way doesn't have a drawback of the first method that I've explained above, however involves some code to be written.

Get URL information in Startup

In an asp.net MVC 5 project I'm using a katana owin based middlewere to handle the authentication. Inside the Start.cs file I have the Startup class with the Configuration method.
Is there a way to get the full URL of the request inside the Configuration method? I need to get the last part of it to be stored in a cookie
public void Configuration(IAppBuilder app) {
app.UseCookieAuthentication(new CookieAuthenticationOptions { ... }
// something here to get the full URL
// other authentication code here
}
Startup runs outside of the request-cycle. In fact, it only runs once, and then multiple successive URLs can be serviced before it ever runs again (when AppPool recycles, server restarts, etc.)
Long and short, even if you could access the URL, it wouldn't do you any good because it would simply be the first random URL that was accessed, which may or may not be applicable to whatever you're trying to do here.

Access Forms Authentication from Separate HTTP Handler

I'm just reading about implementing my own HTTP handler for ASP.NET 4.0 and IIS7. This looks really cool. I want special processing for ZIP files and it seems like an HTTP handler is the perfect solution.
However, what's giving me trouble is that the handler must be in a separate assembly. So how can I access the rest of my application from this assembly?
Specifically, I'd like to determine if the user is authenticated and redirect them to the login page if they are not. But User.Identity.IsAuthenticated, etc. will not be available from my handler.
(Yes, I know there are ways to approach this without an HTTP handler but they don't seem appropriate for my specific needs.)
User.Identity.IsAuthenticated, etc. will not be available from my handler.
The ProcessRequest method gives you the current HTTP context from which you could determine if the user is authenticated:
public void ProcessRequest(HttpContext context)
{
if (!context.User.Identity.IsAuthenticated)
{
// the user is not authenticated
}
...
}

Where exactly does Forms Authentication exist in the Http Pipeline?

Where exactly does Forms Authentication exist in the Http Pipeline?
This is handled by an HTTP module, System.Web.Security.FormsAuthenticationModule. If you look at the system-wide web.config file, c:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG\web.config, you can see where it's mentioned in the <httpModules> section. The site-specific web.config file will inherit the configuration in that file.
On each request, the module will look for an authentication cookie. If it's not present, the request is redirected to the login page. On a successful login, an authentication cookie is sent back to the browser. Then on subsequent requests, the browser will send the cookie, which will be validated by the module, and then the request is handled as usual.
Guess I should've thought of this first but it didn't dawn on me until I saw the answer from #Carl Raymond that I can just crack it open in reflector. So to answer my own question
public void Init(HttpApplication app)
{
if (!_fAuthChecked)
{
_fAuthRequired = AuthenticationConfig.Mode == AuthenticationMode.Forms;
_fAuthChecked = true;
}
if (_fAuthRequired)
{
FormsAuthentication.Initialize();
app.AuthenticateRequest += new EventHandler(this.OnEnter);
app.EndRequest += new EventHandler(this.OnLeave);
}
}
OnEnter calls the private method OnAuthenticate which passes in the application context and this is where it validates/writes out the Form Auth tickets.
In OnExit it checks the response for a Http Status Error Code 401 and if it finds it, that's when it redirects to the Login Url.

Resources