Lecture thumbnail 0:00 / 0:00 Here the question is, do you think that the filters work with minimal API endpoints?
The answer is no. The regular filters that we have learned in the previous
sections, such as authentication filters, action filters, result filters,
all of these doesn’t work with endpoints of minimal API.
But what if you would like to write some before logic and after logic for the
endpoints of minimal API? Exactly for this question, the answer is endpoint filters.
They work almost equivalent to action filters, that is, they contain the before logic as well
as after logic. The before logic executes before the endpoint execution, that is,
mainly used to perform the validation. And the after logic of the endpoint filter executes
after the execution of the endpoint. Generally, we keep the code for validation of parameters
in the before logic. And in the after logic, any kind of final changes to the result,
such as adding additional response or adding additional response cookies or headers, etc.
But fortunately, as of this moment, at least, there are no any categories of endpoint filters.
There is only one type of filter that is supported by minimal API endpoints
that itself is called as endpoint filters. You can always think of action filters while learning
and writing the code for these endpoint filters, because they contain the before logic as well
as after logic. This is the easiest and simple way to create endpoint filter.
After you create your endpoint in minimal API by using any map method such as map
get mapPost, etc. You will add an endpoint by using add endpoint filter method.
Here you have to supply a delegate that receives the context.
This context is of EndpointFilterInvocationContext type, almost equivalent to action
execution context in case of action filters. This context allows you to access the HTTP context,
which includes with the request object, response object, session object, user object, etc.
And also it allows you to access the arguments of the endpoint. For example, there is a model
parameter here or route parameter here. You can access that parameter value as a part of the
endpoint filter so that you can add validation code for the same. Let me show that practically.
Here the goal is we have already created the HTTP post endpoint that is map post over here
and I would like to add validation code for this product. Let’s say for example the id cannot be
blank and name cannot be blank. So first let me add those validation rules in the model class level.
Switch to the model class that is product and add essential validation attributes that is required
and others and import the namespace system.componentmodel.dataAnnotations.
The minimum value is zero and maximum is the integer maximum value and of course the product
name is mandatory. Okay, we have added essential validation attributes as data annotations in this
model class. Now we have to validate these in this map group. So switch to the map group file,
particularly map post method. This is your endpoint delegate or you can say request delegate.
Now at the end you can add endpoint filter and it receives an asynchronous delegate
that contains two parameters EndpointFilterInvocationContext and endpoint filter delegate.
As you can guess the context parameter gives you access to the arguments of the request delegate
and also the HTTP context. For example using this HTTP context you can access the response object,
request object, HTTP context items, session and others as we have seen in the beginning of the
course. For example over here I would like to access the arguments.
So this arguments represents a collection that contains information about all these argument
values including their data types and values. This request delegate has two parameters right
HTTP context and product. Particularly we are interested in the second one which is of product
type. So we can write like of type it is a link method it returns the corresponding argument
object based on the particular data type that is product. So we are querying the particular
arguments collection. Hey arguments collection you have two items with you right one is context
and product. Give me the particular one where the type is product. So it gives the one
and what if you have more than one parameter of the same type?
In that case I would like to get the first one. To say that we have to use a first or default
but what if the product type of parameter does not exist in that request delegate? In that case
it returns null because of the first or default. So we will receive the argument value and we will
check if the product is not equal to null it means it is valid. I mean the product object is found.
Okay in case if it is null we have to return error response right. Return a bad request.
I mean bad request 400 as response. Okay this is the message in the response body along with
the bad request response. But in case if the product object parameter is not equal to null
I mean in the request delegate we have successfully received a non-null value
into this parameter. In that case it will skip this execution of the if statement
and proceed over here. Now again we have to validate and after creating our validation
context in order to perform the validation against the model object create an empty list
of validation result. Into this result we have to load the errors of the model object if any.
Hey built-in class called validator. I would like to validate this particular model object.
If you ask me what is the object to validate it is product object that is one of the parameters
of the request delegate. So we are receiving the parameter of the request delegate and validating
the same. And if you ask me what is the validation context to execute it is the validation context
that we have created before a movement. But in case of any validation errors found
for example required or any other validation is found we have to add essential validation
information into this validation result collection. And yes I would like to validate
all the validation roles not only just required. Means in case of validate all properties as false
it will not perform the validation on other than required attributes.
So true means you have to validate all the properties including all the data annotations.
Okay overall give me a boolean value whether it is valid or not.
But in case if it is not valid then we have to return a bad request.
I mean bad request response with appropriate error message. I would like to get the error
message from the data annotations. In that case you can find all model level validation errors
in this errors collection. Hey errors collection give me the first error message
and particularly error message property of the data annotation.
Say for example the id is not submitted. So the required data annotation will be added as an item
in the errors collection. So we are accessing the first element from the errors collection.
Okay so if the product is null this is the error message with bad request.
In case of validation errors this element. But what if there are no validation errors?
In that case it will proceed to the subsequent code. It will bypass this if block.
So the return statement doesn’t execute. Continue execution.
In that case we have to invoke the next subsequent filter or if no filter exists
then we have to execute the request delegate itself.
See this diagram. Currently we are on the before logic. We are trying to perform the validation of
the particular end point. Still the request delegate of the end point has not executed.
So after validation in your before logic if no validation errors are found then we have to invoke
the subsequent filter if exist. Otherwise execute the request delegate itself. That is what the
meaning of next delegate call. So we are going to call next. So it invokes the subsequent filter
or request delegate of the end point. See suppose you think of more than one filter is applied.
After the execution of the before logic of first filter when you call the next it invokes
the subsequent filter that is filter number two. And as a part of the filter number two if you call
next since there are no other filters it executes the request delegate of the end point itself.
So always the call to the next invokes the end point filter
which is subsequent to the current one or the actual end point request delegate itself.
I mean the actual code that you write here will be executed.
OK. After completion of this request delegate it comes to the after logic.
So whatever the code that is present after the next delegate call that is considered to be
after logic. You can use this after logic as a chance to add any kind of final changes to the response
such as adding response headers or adding response cookies etc.
OK. Anyways in this particular example we have nothing to do in this after logic. So keep it blank.
The next delegate returns the subsequent filter if exist otherwise the request delegate of the
end point itself. And at the end we have to return the same. Because it is the acceptable
return type of the delegate. And by the way we have to supply the current working context
as argument to the next delegate call. The exact point that you should remember is
the end point filter delegate receives the context as argument. You can see that the
next delegate returns an end point filter delegate which represents the subsequent filter.
OK. Let me add the break point in this to see the execution sequence.
Added a break point in the end point filter. Particularly in the before logic as well as in
the after logic. After the next delegate call whatever present that is after logic.
Suppose add the break point in the return statement. And in the same way add a break point
in the request delegate of the end point itself here. OK. Run this application and see how it
performs the validation. Now I am trying to make a POST request. URL is the same port number
products. And the request body contains the JSON information including the product ID
as well as name. I would like to add this product to the existing collection. Now make a request.
OK. At first the before logic executes. See the actual request delegate does not execute.
It identifies the end point since the route matches but doesn’t execute the request delegate
immediately. It first executes the filter because the filter is applied. Now as you can see the
context contains all the arguments. It contains two arguments in this case.
One is the context as well as the product object. As you can see the arg1 that means the second
argument contains a product object with ID and name. And this is what we are trying to validate
here. We have accessed the second argument of the context.arguments into this variable.
So this product refers to an object of the product that is actually the argument value
of the actual request delegate. And since it is not equal to null this if block will be skipped
there. So it continue executing the subsequent code. Here we perform the model level validation
just like what we have done in the service layer earlier. And the result is. Yes it is valid.
Since it is valid that means it has no validation errors. This if block will be skipped.
So no bad request. It simply invokes the subsequent filter or the request delegate itself.
In this case we have added only one filter. So there is no any subsequent filter.
So when you call this next what exactly happens? Yes you are correct. It executes the request
delegate of the endpoint. So the filter execution will be passed there.
And it executes the actual request delegate. So it receives the same product object as you can see
with the product ID as well as name. Same values.
And after that we are adding that product object into the collection
and then return the result. I mean OK result with 200 response status code.
And here is the point. After completion of this endpoint request delegate
it continues executing the after logic of the filter. So in this way it works almost equivalent
to asynchronous action filter. Only the difference is the terms are changed. Instead of calling it
as action filter we are calling it as endpoint filter. That’s the difference. So for understanding
wise coding wise action filters endpoint filters almost play in the same role.
But technically speaking you cannot apply the action filters directly to the minimal API
endpoints. But alternatively you can apply the endpoint filters much like what we have created
as action filters earlier. OK click on continue. So we have executed the before logic first
then the endpoint request delegate itself which contains actual code to execute.
And after that the after logic. And you can see the response over here with status code 200.
But in this application the big complaint is if the endpoint filter has significant
amount of code it will be too cumbersome and lengthy to write everything in the same file.
That is along the side of endpoint code. So it will be better if you can separate the filter
code in a separate file. Something like creating an endpoint filter separately
and applying the same just like what we have done earlier while creating asynchronous action
filters. We will try that in the next lecture. And by the way another point is if more than
one endpoint filter is applied for the same endpoint they execute in FIFO order first in
first out. For example you have applied filter 1 first and then filter 2. So it executes the
before logic of the filter 1 and then before logic of the filter 2 and then the request
delegate of the endpoint. Then in case of after logic the same will be reversed obviously.
So then it executes the after logic of the filter 2 and then after logic of the filter 1
as reversed to the FIFO. At your practice time
you try to create and apply more than one endpoint filter to the same endpoint.