The middleware component are executed in order, and it usually makes sense to run through all the components in the pipeline, until one of them decides to cut the pipeline by not calling the next one.
This is possible because each middleware component has a reference to the next one, and the environment, a dictionary of objects, whose key is a string, who gives acces to all the necessary things to check the request, create the response, and do many other things. (using a dictionary is clever, because it's easy to add any kind of information, or executable code, which isn't known beforehand).
Each component can do the following things, but all of them are optional:
- execute code before calling the next component (usually checking and modifying the environment dictionary)
- call the next middleware component of the pipeline (which will do the same)
- execute some code after the next component finishes executing
- throw an exception (intentionally or because of un unhandled error)
The pipeline execution will finish on one of this cases:
- a middleware component doesn't call the next one
- a middleware component throws an exception
NOTE: each "Middleware component" is an implementation of the Application Delegate
Having a bunch of components makes sense, if they're are registered in the right order. A pipeline could look something like this:
- authentication
- authorization
- caching
- execution of some kind of API or HTML generation
Each component has enough information to execute the code its responsible for. For example:
- authentication will alwasy be executed, and set in the dictionay the information of the principal. Then it will cal lthe next one: authorization component
- depending on the request, and the result of the authenticacion, the authorization component will decide if the principal has permissions for executing the request, and call the next component (caching), or reject the request
- the caching can also decide if it can return cached results, or has to call the next component
- the last component will execute, and will probably create the response, and it will not call any other component. The call stack will be run in reverse order, giving each component the possibility to do some estra work. For example the caching component could cache the response, and the next ones (authorization and authentication) will simply return without executing any extra code.
As each component has all the request, response, context and any other desired information, they all have enough information to decide if they have to do something, modify the dictonay, cal lthe next one or retunr...
So, as you can see, registering a lot of middleware components doesn't require all them to be executed for each request, but it sometimes make sense.
OTOH executing a middleware component can be really cheap. For example, authentication could simply execute the next component if the request doesn't require authorization. And the same with the cahing component, which could simply call the next component if caching is not reuired.
So even if a component it's in the pipeline it can have a cheap execution, or it can even not run at all depending on the request.
In your particular question of static files, and Web API, one of the components will be resgitered before the other. The first one, whichever is, will execute and create the response, or simply call the next one, depending on the request. E.g. if the static files is registered before the other one, if a file is requested the static file will create the response, and will not call the Web API. If the request isn't for a file, it will do nothing but calling the next componente: the Web API component.