Creating an OData v4 API with ASP.NET Core 2.0

Hallo Mensen šŸ˜€ Let’s talk OData, shall we?

In the last few years my work revolved a lot around REST APIs. I think most of us will agree that REST APIs are a really good way of obtaining data from a server without caring too much about details on how to get access to that data.

You call an URL with the proper parameters (including auth or not), headers, and HTTP verb and you get some data back. Easy enough, right?

The negative side is that these APIs are either very rigid on the way they are implemented, meaning that you can only get the data in the exact way defined by the server, or they are very verbose on their implementation, meaning that you will haveĀ a lot of code to maintain on the server side to make the API flexible.

Then what?… Use OData!

What is the Open Data Protocol (OData)?

OData Logo
OData Logo

OData, orĀ OpenĀ Data Protocol, is a set of ISO approved standards for buildingĀ and consuming RESTul APIs, but what does it mean, in practice?

It means that OData is not really an implementation that you simplyĀ use but documentation specifying how you must implement it yourself. You can read a lot more about OData here.

Why would I want to use OData?

There are a number of benefits that you will obtain from implementing OData standards, from an easier learning path on the usage of your API by your customers/consumers, to the fact that OData is easily readable by machines, and in this post I want to talk about the flexibility that implementing OData gives to your API through the OData URL Conventions.

Using URL Conventions you can expose a much cleaner and generic API and let the consumer specify their needs through the call.

OData URL Conventions

The OData URL Conventions are a set ofĀ Ā commands that you can pass to the API through the HTTP call query string.

An OData URL is typically composed by three parts: service root URL, resource path and query options.

OData Url Format

  • Service Root URL is the root address for the API.
  • Resource Path identifies exactly which resource you are trying to achieve.
  • Query Options is how you define to the API the format in which you need that data to be delivered.

How does that sound? Simple enough, right? but also, with the proper options, extremely powerful. Let me give you a list of possible options within the Query Options block of the call:

  • $select: Allows you to define a subset of properties to return from that Resource.
  • $expand: Allows you to include data from a related resource to your query results.
  • $orderby: Not surprisingly, allows you to define the ordering of the returned dataset.
  • $top: Allows you to select the top X results of the query.
  • $skip: Allows you to skip X results of the query.
  • $count:Ā Allows you to get a count of items that would result from that query.
  • $search:Ā Allows for a free-text search on that particular resource
  • $format: Allows you to define the format for the returned data in some query types
  • $filter: Allows you to define a filter for your dataset.

As you can see, many of the commands are pretty similar to what you have in most of the common query languages.

I will go into a bit more detail in each of those options on the Code sample.

OData and ASP.NET

ASP.NET CoreĀ still don’t have a stable library to implement the OData protocol! But worry you not, as Microsoft has been working on it for some time and right now we have a really promising beta version on Nuget. You can find it here.

ASP.NET Framework has a really good library to implement OData, and it is quite stable by now. You can find it here.

Enough with the theory, how can we implement this query protocol in my ASP.NET Core Application?

letscode

Implementing your API

Let’s start creating a simple ASP.NET Core Web API Application on Visual Studio andĀ creating our models.

Also, let’s create our DbContext…

…and configure our Services.

Good! Our plumbing is set, now we are going to seed some initial data to our database.

And now we call our seeder on app startup.

We must not forget to add our migration.

And last, but not least, let’s implement the simplest API possible on our 3 entities.

Done. Now we can test it using Postman:

Ā 


Wow! It certainly looks like a nice API, doesn’t it? What is the problem with it? Why would I ever want to add OData to it?

Well, there are two fundamental problems with this approach to our API: the payload size and the querying of the data from the database.

Payload Size

The payload format is completely defined on the API/Server side of the application and the client cannot define which data he really needs to receive.

This can be made more flexible by adding complexity to the code (more parameters? more calls?) but this is not what we want right?

In the most common scenarios the client will simply have to ignore a lot of data that he doesn’t care about.

Look at the result of our query for Books below and tell me what should we do if I only want theĀ name for the first book on the list?

We have no options here other than accept all this data and filter what we need on client side.

Querying the Data

For much of the same reason, all the queries to the database have to be done in a very rigid way, not allowing for smaller queries whenever possible.

In the same query as above, we just sent a request for a list of books, and let’s have a look at what was sent to the database:

All this huge query just to get the name of one book. That doesn’t sound good, right?

Let’s make it better with OData šŸ™‚

Changing our API to OData

The good news is that we can use much of the same structure for our OData API, we just need to make a few configurations. Let’s start by installing the package.

As you can see the OData package for .NET Core is still in beta, as opposed to the .NET Framework version of the package, who is stable for a long time already. I have high hopes that this package will be out of beta in no time!

Let’s configure our entities to understand the OData commands.

I commented the function of each call on this class, pay close attention to it, as the calls are paramount for the full use of the URL Conventions. And now let’s wire our OData configuration with the rest of the API.

All good! Finally, we must adjust our three controllers and have then accept OData URLs. Things you should notice were changed on the Controllers:

  • All the controllers were renamed to be in the singular form. That is only necessary due to our configuration in the ModelBuilder, they can be configured to be plural.
  • The return types were all changed to IQueryable<T>
  • The .Include()Ā calls were removed, as they are no longer necessary. The OData package will take care of this for you.
  • We are no longer inheriting from ControllerĀ but from ODataController
  • We have a new decorator for the API calls: [EnableQuery]

And that is it! Our API is ready to be used with OData URL Conventions. Let’s try it?

New API and Results

You can play with the new API format on Postman:

Ā 

The original call to get books would look like this:

http://localhost:5000/api/books

The new call will look like this

http://localhost:5000/odata/Book

First of all, let’s try to get a list of books and look at the results:

MUCH Cleaner, right? Let’s even make it smaller, as we just want the name of the first book on the list:

http://localhost:5000/odata/Book?$top=1

And let’s have a look at the database query for this call:

Yes! A much more efficient call! But wait… We just need the NAME of the book, why don’t we make it more specific?

http://localhost:5000/odata/Book?$top=1&$select=Name

And that is an awesomely small payload! And the query is also more efficient

What if you want details about and specific Author and all his related books?

http://localhost:5000/odata/Author?$expand=Books&$filter=Name eq ‘J.K. Rowling’

Amazing, isn’t it? That can really increase the quality of our APIs.

As a last piece of information, let’s not forget that OData is designed to be readable by machines! So we have a couple of out-of-the-box urls with documentation on our API:

http://localhost:5000/odata/$metadata

Cool, isn’t it?

What’s Next?

Well, now that you know how simple it is to implement the OData protocol in .NET, I would recommend that you spend some time getting familiar with the Protocol itself, you can find all the guidance that you need here.

Also, if you have the intention to use the protocol with .NET Core, I suggest that you keep a close eye on the Nuget Package page and also the feature request on GitHub.

Source Code

You can find the whole source code for this solution on my GitHub.

And that is all for today folks šŸ™‚

I hope you enjoyed it, and don’t hesitate to use the comments section if you were left with any questions.

Sources:

.NET Core 2.1 is coming! (and I will be back)

Hallo Mensen šŸ™‚
I know I’ve been away from my blog for a long time and I’ll not try to make an excuse for this, but I want to make it clear that I intend to start writing again some time this quarter!

Today I just wanted to share with you two new videos from Channel 9 with some cool demos on the new features for .NET Core 2.1. In particular I would advise you to pay close attention to the improvements on the HttpClient and the Entity Framework support for CosmosDb. Enjoy!

What is new in .NET Core 2.1?

The Demos!!

One last thing to mention. Pay close attention to the benchmarks on the build process for .NET Core 2.1, it is amazing!

Incremental Build Improvements for .NET Core 2.1 SDKReally excited for the Future of .NET Core šŸ˜€

.NET Core 2.1 should have its first previews released still in this February and the RTM Version is planned to this Summer!

Source: .NET Core 2.1 Roadmap | .NET Blog

 

[ASP.NET Core MVC Pipeline] Controller Initialization – Action Selection

So, we just finished looking at the Routing Middleware, and that also completes our walk-through the Middleware Pipeline! What happens next? Now we enter the realm of the Controller Initialization, and the first thing we need to do is theĀ Action Selection. Let’s revisit our MVC Core Pipeline flow.

The ASP.NET Core MVC Pipeline
The ASP.NET Core MVC Pipeline

We will now focus on the green part of our pipeline, theĀ Controller Initialization.

Controller Initialization
Controller Initialization

The objective of the process in the green box is:

  1. Find the most suitableĀ Action in the application for that request
  2. Call the Controller Factory informing the requiredĀ Action
  3. Get an instance of aĀ Controller from the Controller Factory

That is all it does, and it is a very important job šŸ™‚ But, how can we interfere with this process? The first thing we can do is add some rules to make theĀ Action Selection behave as we want it to.

The easiest way

You don't have to customize everything
You don’t have to customize everything

It’s true! You probably already used some alternatives toĀ bend the process of Action Selection towards your objectives. The most common method is to use the Verb Attributes. Let’s imagine you want to create 4 actions in a single controller and each of them will respond to a different HTTP Verb:

What is wrong with this code? All the methods claim the same action path on the route, and the Action Selector has no good way to define which one to call! What will happen when we try to access the /index route?

Ambiguous Exception
Ambiguous Exception

Ambiguous Exception! and that happens, as you can see in the underscored line, because the framework does not have enough information to decide which one is the best candidate action, and that is where we can use aĀ MethodSelectorAttribute:

Now the framework will know the best action to choose based on the HTTP Verb of the Request šŸ™‚

That code exemplifies the kind ofĀ intervention that we can do in the process of choosing the most fitting Action Method. But, what if we want to change this behavior in a way that is specific to some kind of logic that we envisioned? That is when you should think about adding anĀ Action Constraint.

What is an Action Constraint?

An Action Constraint is a way that we have to tell theĀ Action Selection process that some method should be a better candidate than the other options for that request. It is really that simple. An action constraint is a class that implements the following Interface:

TheĀ OrderĀ property will help you define the priority in which that constraint must be evaluated and the Accept method is where the true logic is implemented. Whenever an ActionConstraint is evaluated and the return of the Accept method is TRUE, then it will tell theĀ Action Selection process that this Action is a better suitable match for the request.

Customizing the Action Selection – Custom Action Constraint

Now let’s implement our own IActionConstraint and force theĀ Action Selection process to work as we want it to. Let’s imagine a scenario where we want to serve specific content to our users who access our application through a Mobile Browser, and we want to handle that on the back-end, as we really will serve different data to this users. In this situation we have the following Action Methods:

That would, again, give us the AmbiguousException because, as it is, is impossible to the framework to choose a better Action between those two, so what can we do to help? Let’s implement our action constraint:

I know, I know… there are sure better ways to implement this behavior and that not fool-proof at all, but it is enough for our intents. We set theĀ Order property of our Action Constraint to “0” so it will be one of the first to be evaluated by the framework, and the implementation of our Accept Method returns true if the request’s user-agent container either “Android” or “iPhone” in its value.

So, how to hookup this component to our pipeline? Easy enough:

Ha! Simple, isn’t it?

Results

Default Content
Default Content

When accessing through a common browser, you are going to be redirected to the default implementation of our View…

Mobile Content
Mobile Content

…and when accessed through a Mobile Browser, you will be presented with the specific implementation of our View. Cool, right?

This is one of my favorite pluggable components in the entire framework pipeline. It doesn’t feel too much invasive, and it can help us bend the flow of the request in a very useful way!

What do you think of it? Can we think of a way to use it on your applications?

Source:

[ASP.NET Core MVC Pipeline] Routing Middleware – Route Handler

Let’s move on with our series about the Core MVC Pipeline. What is the next step? Last time we learned how to point a request to a specific Router, and what if we need to handle a route in a custom way? Let’s create our custom Route Handler.

Middleware Pipeline
Middleware Pipeline

As we can see, the Route Handler is the last step inside our Routing Middleware and, as long as the request matches any of the routes, is also the last step inside the Middleware Pipeline, we are almost ready to move on to the next phase on the Core MVC Pipeline.

But what exactly does the Route Handler do? Let’s think functionally:

  1. Our Web Server received a request and, since it is a valid request, passed it to our application.
  2. Our application passed the request to our Middleware pipeline which running the request through every middleware until one of them decides to end the request.
  3. The request went through a lot of processes and reached our Routing Middleware.
  4. Our routing middleware will pass the request to our Router who will see if this request matches any known route. If it doesn’t, the middleware will pass the request to the next middleware, or initiate the response.

But what if the requestĀ matches any known route? What is next? That is where our Route Handler gets to do its magic!

The Route Handler is the component that defines what to do with the Request that matches a Route on our system. The default implementation for this component is theĀ MvcRouteHandler, which usually handle every single request accepted by the Router, but now we are going to implement our own Route Handler.

Let's Code
Let’s Code

The Code

This component suffered a great deal of changes in the way it is structured on the Full .NET Framework in comparison with the .NET Core implementation.

To implement our Route Handler we are simply going to need a RequestDelegate. This request delegate will contain all the logic that must be executed against a matched route and create a response.

The code for this example will be very simple, as this is a very specialized component, and I don’t see it getting overriding frequently.

It is this simple! In this example any request matched and sent to our RouteHandlerĀ will redirect the user to this Blog šŸ™‚ The object “c” is a HttpContext object, so anything that can be done with a request/response flow can be done in the RouteHandler as well.

Now we only have to hook up ourĀ RouteHandler on the Routing Middleware:

In the case (line 3) we created a Route that will match ANY request in which the URI starts with “blog/” and send it to our custom router, which will redirect the request.

And that is all there is for a simple custom router šŸ™‚
There is of course other ways to implement Route Handlers but this should be more than enough for the very specific cases where this is necessary.

This wraps up our talk about the Middleware part of the MVC Pipeline! In the next post we start the talk on the Controller Initialization!

See you next time šŸ™‚

That's All Folks
That’s All Folks

 

[ASP.NET Core MVC Pipeline] Routing Middleware – Custom IRouter

What if you want to add some custom logic to the routing system?

There are two main components where we can inject our code to bend some rules on the framework. The first one is the IRouter.

Middleware Pipeline - Routing
Middleware Pipeline

In our flow the IRouter is represented by the “Matching to Route Entry” box and that is what it is. The role of the IRouter in the Routing system is to match something on a request, usually on the URI, and point it to the rightĀ IRouteHandler. We are going to learn more about theĀ RouteHandler component in a future post.

Why would I do that?

As previously said, the role of the IRouter in the pipeline is to evaluate the request and pass it to theĀ IRouteHandler that is the best fit, but what might be mentioned is that you can also use the IRouter to make modifications to the incoming request. In this way, whenever you have a request that matches a certain criteria you can add, remove or edit some information and let the default RouteHandler do the rest.

In my experience, that is the most common case. I don’t see complete custom implementation of the Routing system so frequently.

The Basics

A bit different from the custom middleware, which is entirely implemented through conventions, the custom Router must implement an Interface called IRouter. This interface is quite simple:

The first method,Ā GetVirtualPath, is used internally by the framework to generate urls based on this route in methods like theĀ HTML.ActionLink one, the second method, RouteAsync,Ā is where our logic will really reside.

Our class should start with something like this:

Routing our Requests

The concept behind theĀ RouteAsync method is quite simple and straightforward, we do all the checking necessary to see if the provided route matches our IRouter context and, if it does, we execute the needed logic and inform the Routing middleware that the request has been handled.

We are doing theĀ checking part on the if statement where we analyze if the request comes from a Mobile User-Agent (there are certainly better and more secure ways to do this but it is enough for the purpose of the demonstration) and if our Router finds that the request comes from such an agent, we apply some logic to change theĀ controller andĀ action values in ourĀ RouteData, therefore ensuring that our request will be redirected to the right Action.

If everything works as expected, then we send our modified context to the Default RouteHandler, and let it process it. The RouteHandler will set the value for theĀ Handler property in our context, and it will let the framework know that the request has been Handled by our Router and no longer need to be processed by other Routers.

Hooking up to the Routing Middleware

Now we have a nice and functional CustomĀ Router but if you run the application you will see that it is never touched by your request and that is because we never hooked our Router to the Routing Middleware.

The Routing Middleware has a stack of Routers that it will use to try and match a request, hence we need to add our Custom Router to this Stack. Let’s do it.

Now we add a new Router to theĀ Routes collection and we passed the Default Handler, which will be used by our Routing flow.

And that is all we have to do to make our Router capture every request that comes from a mobile User-Agent and redirect them to specific controllers/actions.

To be completely honest, creating a custom Router will usually be a bit of an overkill as you can achieve most of the desired behaviors through the usage of easier components on the pipeline (including the middleware), but I think that is important to go through all the major components that we can customize.

What do you think? Sounds like something that you can use in your application?

Thanks for Reading! Tot ziens šŸ™‚

MVC Pipeline Series
Sources

[ASP.NET Core MVC Pipeline] Middleware Pipeline

Let’s play with the Middleware Pipeline šŸ™‚

Do you guys remember the flow that I showed you in the first post? No? Let’s revisit it:

The ASP.NET Core MVC Pipeline
The ASP.NET Core MVC Pipeline

So, what do we want to do here is to start from the beginning and in this post we are going to focus on the Blue part of the flow, the Middleware Pipeline:

Middleware Pipeline
Middleware Pipeline

What is a Middleware?

So, what is a middleware in the context of the Core MVC Pipeline, and why should we care about it?

Middleware is software that is assembled into an application pipeline to handle requests and responses.Ā Each component chooses whether to pass the request on to the next component in the pipeline, and can perform certain actions before and after the next component is invoked in the pipeline.

Hmmm… so each middleware is actually a piece of code, which follow some patterns, that we can ‘pile’ forming what we call a pipeline. Each of those pieces of code can (and should) implement its own logic and, that is the most interesting part, can decide whether or not to pass on a request! Because of that, each middleware is empowered to end an HTTP request and ignore every other middleware after if it decides to. Let me draw it for you:

 

Middleware Pipeline
Middleware Pipeline

In the example on the left, the request pass through all the middleware in the Pipeline and finally gets answered by the Routing Middleware.

In the example on the right, the request is cut short by a custom middleware that send the response itself rather than pass it along to next one.

A couple of important things to notice:

  • A middleware can execute successfully its function and still pass the request to next middleware, therefore allowing for more changes before sending the response.
  • The routing middleware is certainly an important one but he is not vital on the middleware pipeline. It is vital to the MVC Framework, but you could have a perfectly working application without the routing middleware.

Our first Middleware

The sequence of middleware is defined on theĀ Configure method of theĀ Startup.csĀ class.

Each of the “Use…” methods define a middleware. Let’s inject our own super simple middleware on the stack:

In this case we are defining that only the requests sent with the HTTP methodĀ GET are going to be pass on to the Routing Middleware. Other HTTP methods will be answered directly by our middleware and will never reach the Routing system. Notice that we defined our middleware inline, inside theĀ Configure method and that is not a good practice because it worsen readability and break the Single Responsibility principle.

Pretty simple, ahn? Let’s get into some details.

Order Matters

Keep it in mind, the order in which your middleware is set up inside theĀ Configure method matters! It matters because this is the order in which they are going to be executed when a request is received. In our example our custom middleware would never be hit if the user requests an existing static resource (like an image, or a JS file) because theĀ UseStaticFiles middleware would have handle the request and sent a response to the user shortcutting the request.

Before AND After the next Middleware

We can see in our example that the execution of the next middleware in the pipeline is represented by a call to theĀ next() delegate, and that means that we can execute some logic before and after the next delegate execution.

Use, Run or Map

Every middleware must run using one those three methods that define a specific behavior for the middleware.

Use

First of all, the most common method, the ‘Use…‘. This method is the one that allows our middleware to receive a request, process it and, if necessary, pass it on to the next middleware on the pipeline.

Use(IApplicationBuilder, Func<HttpContext, Func, Task>)

We receive a delegate with an HTTPContext with all the details available about the request and another delegate, which is actually the call to the next middleware. As a result we can call the next middleware in the pipeline whenever we want or not even call it, if our logic says it is not necessary.

Run

Then we have the ‘Run…‘ method and this method allows us to define a middleware that willĀ always end the execution, simply because it has no knowledge of a next middleware in the pipeline.

Run(IApplicationBuilder, RequestDelegate)

In this case, the only thing we receive is the delegate with the custom logic of our own middleware.

Map & MapWhen

And then we have the ‘Map…‘ method which, as the name implies, allows us to map a request to a middleware depending on theĀ Path of the request.

Map is a little bit different from the previous methods in the sense that you only map the request to a middleware, but you are still going to need to set an ‘Use…‘ or ‘Run…‘ method to actually execute our logic.

Map(IApplicationBuilder, PathString, Action)

So theĀ Map method actually pass on an instance of the IApplicationBuilder interface! Which allows us to define a new middleware depending on the mapped request.

The Map method also has a variant called MapWhen that allows us to map the middleware against some condition on the HttpContext.

So, those four methods are supposed to give us all the flexibility that we need to create our middleware.

I think it is possible to notice that creating a middleware inline is not a great idea, right? It gets messy really quick and we don’t want that. Let’s start creating a real Middleware in a more decent way.

Creating our Custom Middleware

First of all we need to do is to create a new class and, to do that, we are going to follow some simple conventions. Our Middleware’s class and filename must end with the word ‘Middleware’:

This code, by itself, is already a working middleware. It won’t do anything, except calling the next middleware, but it will work. šŸ™‚

Now I expect you to be asking “Where is the Base Class?”, “You forgot to implement the interface?” or “Where are the overridden methods?” and believe, I asked myself these exact same questions. The answer is simple, the framework will understand and use our Middleware based on the conventions we followed:

  • The Middleware must have a Constructor that accepts one RequestDelegate
  • The Middleware must have a public Method that receives an HttpContext as parameter and returns a Task type.

Pretty straightforward, right?

Let’s implement some logic to our Middleware. Worth mentioning that there should be better ways to implement what we are doing here, but for the purpose of the Post Series, it should be enough.

Basically what our middleware is doing is making sure that only requests with the “GET” method ever reach the next middleware and if any other method is used than the request will be shortcut.

Looks much better than having all this codeĀ inline right? But we are not ready yet!

Hookup with the Middleware Pipeline!

While this is a working code for a middleware we haven’t hooked this up to our pipeline. Let’s do this now. Back to ourĀ Startup.cs Class, in our configure method, we want our new middleware to be added like this:

Looking really good now, isn’t it? Let’s make look more like the rest of the middleware pipeline by creating one extension method for our middleware.

And now we can set up our Middleware like this:

And that is it! We have a custom middleware setup and working. Our pipeline now should work like this:

Middleware Samples
Middleware Samples

And that is it for the first post of the Core MVC Pipeline. What do you think? Can you imagine some scenarios where changing the Middleware Pipeline would be useful for your application?

On the next post we will start talking about the Routing Middleware, where we can introduce some logic to change the Routing rules šŸ™‚

Tot ziens!

Source:

The ASP.NET Core MVC Pipeline

Hallo Iedereen šŸ™‚
I decided to start a series of posts about custom components for the ASP.NET Core MVC Pipeline.

Most of you should know by now that ASP.NET Core was mostly redesigned to be more modular, extensible and to deal with the ‘platform agnostic’ nature of the new framework. This redesign also allows us to customize the way every request is processed, adding custom components in each step of the pipeline.

The ASP.NET Core MVC Pipeline

The ASP.NET Core MVC Pipeline
The ASP.NET Core MVC Pipeline

The idea is to have one post for each of the small blocks on the pipeline, precisely because it is possible to hook up your custom code in any of those points.

I’ll start by providing a brief overview of which is the role of each block on the pipeline:

Middleware Pipeline

Middleware Pipeline
Middleware Pipeline

The middleware pipeline is the entry point for each request in an HTTP Request, it is basically a stack of components that can execute some logic and decide whether to pass the request to the next middleware, or to send the response back to the client.

Routing Middleware

The routing middleware is just one of many that can be used by the ASP.NET Core but is of major importance to the Core MVC Pipeline. The reason for this it that this is the Middleware that ‘captures’ an incoming request and really starts the MVC Pipeline. If an incoming request url does not match any registered Route the request is passed onward to the next Middleware, ignoring the MVC Pipeline, but when the request url matches a known route, that is when the MVC Pipeline really begins.

Controller Initialization

Controller Initialization
Controller Initialization

Once a route is found by the Routing middleware, the next step is to find a controller that matches the route and instantiate it. The process of finding the best suitable Controller and Action for a Route can also be customized.

Action Execution

Action Execution
Action Execution

Now we are in the core of what really is the MVC pattern. The Action Execution is the process that will really process the incoming request and, after that, generate a Result. This is also the process where we can hook up most of our components.

Model Binding

One of the greatest features on the MVC Framework is the ability to bind request data to POCO Objects. I’ll try to explain a bit about how the standard Models Binders work, and how you can create your own Model Binder.

Filters

Filters are components that can be injected at many stages of the Action Execution and that will allow us to define behaviors that we want in our Actions, but that are not necessarily part of the LOB Logic.

Action Result

Finally, the action result is the last step of the Action Execution, and it involves basically to create a ‘Result’ type that will be processed by the Result Execution.

Result Execution

Result Execution
Result Execution

And the last step in the Core MVC Pipeline has arrived! Here is where our ‘Result’ will be analysed and processed accordingly. Here we can also inject some filters before and after our processing, allowing us to change the way the result will be processed.

Where do we start?

From the beginning! Of course šŸ™‚ We will start creating a Custom Middleware that will be executed before the Routing Middleware.

The first post will be up very soon!

Tot ziens! (And I just spent all my Dutch in this post)

Source: