✨ Looking For Sponsors ✨
FastEndpoints needs sponsorship to sustain the project. Please help out if you can.
New 🎉
Support for cancellation of queued jobs
Queuing a command as a job now returns a Tracking Id with which you can request cancellation of a queued job from anywhere/anytime like so:
var trackingId = await new LongRunningCommand().QueueJobAsync();
await JobTracker<LongRunningCommand>.CancelJobAsync(trackingId);Use either use the JobTracker<TCommand> generic class or inject a IJobTracker<TCommand> instance from the DI Container to access the CancelJobAsync() method.
NOTE: This feature warrants a minor breaking change. See how to upgrade below.
Check if app is being run in Swagger Json export mode and/or Api Client Generation mode
You can now use the following new extension methods for conditionally configuring your middleware pipeline depending on the mode the app is running in:
WebApplicationBuilder Extensions
bld.IsNotGenerationMode(); //returns true if running normally
bld.IsApiClientGenerationMode(); //returns true if running in client gen mode
bld.IsSwaggerJsonExportMode(); //returns true if running in swagger export modeWebApplication Extensions
app.IsNotGenerationMode(); //returns true if running normally
app.IsApiClientGenerationMode(); //returns true if running in client gen mode
app.IsSwaggerJsonExportMode(); //returns true if running in swagger export mode[AllowFileUploads] attribute for endpoint class decoration
When using attribute based configuration of endpoints you can now enable file upload support for endpoints like so:
[HttpPost("form"), AllowFileUploads]
sealed class MyEndpoint : Endpoint<MyRequest>
{
}Blazor Wasm support for Remote messaging client
A new package has been added FastEndpoints.Messaging.Remote.Core which contains only the core functionality along with a client that's capable of running in the web browser with Blazor Wasm.
Bypass endpoint throttle limits with integration tests
Integration tests can now bypass the throttle limits enforced by endpoints if they need to like so:
[Fact]
public async Task Throttle_Limit_Bypassing_Works()
{
var client = App.CreateClient(new()
{
ThrottleBypassHeaderName = "X-Forwarded-For" //must match with what the endpoint is looking for
});
for (var i = 0; i < 100; i++)
{
var (rsp, _) = await client.GETAsync<ThrottledEndpoint, Response>();
rsp.IsSuccessStatusCode.Should().BeTrue();
}
}Each request made through that client would then automatically contain a X-Forwarded-For header with a unique value per request allowing the test code to bypass the throttle limits set by the endpoint.
Improvements 🚀
Change default redirection behavior of cookie authentication middleware
The default behavior of the ASP.NET cookie auth middleware is to automatically return a redirect response when current user is either not authenticated or unauthorized. This default behavior is not appropriate for REST APIs because there's typically no login UI page as part of the backend server to redirect to, which results in a 404 - Not Found error which confuses people that's not familiar with the cookie auth middleware. The default behavior has now been overridden to correctly return a 401 - Unauthorized & 403 - Forbidden as necessary without any effort from the developer.
Change 'RoleClaimType' of cookie auth to non-soap value
Until now, the CookieAuth.SignInAsync() method was using the long soap version of 'Role Claim Type' value http://schemas.microsoft.com/ws/2008/06/identity/claims/role which is not in line with what FE uses for JWT tokens. Now both JWT & Cookie auth uses the same value from the global config which is set like below or it's default value role:
app.UseFastEndpoints(c=>c.Security.RoleClaimType = "role");Workaround for CookieAuth middleware 'IsPersistent' misbehavior
By default, in ASP.NET Cookie middleware, if you specify an Expiry or Max-Age at the global/middleware level, setting IsPersitent = false will have no effect when signing in the user, as the middleware sets Expiry/Max-Age on the generated cookie anyway, making it a persistent cookie. A workaround has been implemented to fix this behavior.
Fixes 🪲
[HideFromDocs] attribute missing issue with the source generator
If the consuming project didn't have a global using FastEndpoints; statement, the generated classes would complain about not being able to located the said attribute, which has now been rectified.
Service registration source generator issue with third party source generators
The service registration source generator was encountering a compatibility issue with partial classes generated by other source generators such as Mapster, which has no been fixed.
Swagger UI issue with path param names that are substrings
If a route contains multiple path parameters where one is a substring of another, the generated swagger spec would cause Swagger UI to not match the path param correctly. An example of this would be a route such as the following:
/api/parents/{ParentId}/children/{Id}
Path segment matching has been changed to include the parenthesis as well in order to prevent substring matching.
Default values for Enum properties not displayed in Swagger UI
Enum property default values were not being displayed in Swagger UI due to incorrectly generated Swagger Spec due to a bug in NSwag. A workaround has been implemented to generate the correct spec.
Incorrect property name detection for STJ deserialization exceptions
When deserializing Enum arrays from query parameters, if one of the values is invalid, the following error response was generated with an incorrect property name:
{
"statusCode": 400,
"message": "One or more errors occurred!",
"errors": {
"0]": [
"The JSON value could not be converted to MyEnum. Path: $[0] | LineNumber: 0 | BytePositionInLine: 9."
]
}
}Which has been now corrected to provide a better error message and the correct property name as follows:
{
"statusCode": 400,
"message": "One or more errors occurred!",
"errors": {
"myEnumValues": [
"Value [Xyz] is not valid for a [MyEnum[]] property!"
]
}
}Default response/produces metadata was ignored for 'IResult' response types
There was an oversight in adding default response metadata to endpoints that were returning 'IResult' types, which has now been rectified.
Minor Breaking Changes ⚠️
Job storage record & storage provider update
In order to support the new job cancellation feature, the following steps must be taken for a smooth migration experience:
- Purge all existing queued jobs from storage or allow them to complete before deploying new version.
- Add a
Guidproperty calledTrackingIDto the job storage record entity and run a migration if using EF Core. - Implement
CancelJobAsync()method on the job storage provider