ServiceStack's Global Request and Response filter lets you apply your own generic custom behavior to ServiceStack Requests.
These should be registered in your AppHost.Configure()
Startup:
Global Request Filters​
The Request Filters are applied before the service gets called and accepts: (IRequest, IResponse, RequestDto) e.g:
//Global Request Filter to check if the user has a session initialized
this.GlobalRequestFilters.Add((req, res, requestDto) =>
{
var sessionId = req.GetCookieValue("user-session");
if (sessionId == null)
{
res.ReturnAuthRequired();
}
});
Async​
Use GlobalRequestFiltersAsync
when you need to make async requests in your Global Request Filters, e.g:
Simplified example of ServiceStack's Validation Feature:
//Global Async Request Filter to automatically validate a Request DTO
this.GlobalRequestFiltersAsync.Add(async (req, res, requestDto) =>
{
var validator = ValidatorCache.GetValidator(req, requestDto.GetType());
if (validator == null)
return;
var validationResult = await Validate(validator, req, requestDto);
if (validationResult.IsValid)
return;
var errorResponse = DtoUtils.CreateErrorResponse(requestDto, validationResult.ToErrorResult());
await res.WriteToResponse(req, errorResponse);
});
Tip
If you're writing your own response to the response stream inside the response filter, add res.EndRequest();
to signal to ServiceStack not to do anymore processing for this request
Simple Rate-limiting example:
GlobalRequestFiltersAsync.Add(async (req,res,dto) => {
var response = await client.Send(new CheckRateLimit {
Service = dto.GetType().Name,
IpAddress = req.UserHostAddress,
});
if (response.RateLimitExceeded)
{
res.StatusCode = 403;
res.StatusDescription = "RateLimitExceeded";
res.EndRequest();
}
})
Global Response Filters​
The Response Filters are applied after your service is called and accepts: (IRequest, IResponse, ResponseDto) e.g:
//Add a response filter to add a 'Content-Disposition' header so browsers treat it as a native .csv file
this.GlobalResponseFilters.Add((req, res, responseDto) =>
{
if (req.ResponseContentType == ContentType.Csv)
{
res.AddHeader(HttpHeaders.ContentDisposition,
string.Format("attachment;filename={0}.csv", req.OperationName));
}
});
Global Request and Response Filters​
PreRequestFilters
- Global Filter executed at the start of all ServiceStack RequestsGlobalRequestFilters
- Global Request Filter for ServiceStack Service RequestsGlobalResponseFilters
- Global Response Filter for ServiceStack Service RequestsGatewayRequestFilters
- Request Filter for Service Gateway RequestsGatewayResponseFilters
- Response Filter for Service Gateway RequestsGlobalMessageRequestFilters
- Request Filter for Service MQ RequestsGlobalMessageResponseFilters
- Response Filter for Service MQ Requests
Async versions are also available:
GlobalRequestFiltersAsync
- Global Request Filter for ServiceStack Service RequestsGlobalResponseFiltersAsync
- Global Response Filter for ServiceStack Service RequestsGatewayRequestFiltersAsync
- Request Filter for Service Gateway RequestsGatewayResponseFiltersAsync
- Response Filter for Service Gateway RequestsGlobalMessageRequestFiltersAsync
- Request Filter for Service MQ RequestsGlobalMessageResponseFiltersAsync
- Response Filter for Service MQ Requests
Typed Request Filters​
A more typed API to register Global Request and Response filters per Request DTO Type are available under the RegisterTyped*
API's in AppHost where you can register both typed Request and Response Filters for HTTP and MQ Services independently:
void RegisterTypedRequestFilter<T>(Action<IRequest, IResponse, T> filterFn);
void RegisterTypedResponseFilter<T>(Action<IRequest, IResponse, T> filterFn);
void RegisterTypedMessageRequestFilter<T>(Action<IRequest, IResponse, T> filterFn);
void RegisterTypedMessageResponseFilter<T>(Action<IRequest, IResponse, T> filterFn);
Here's an example usage that enables more flexibility in multi-tenant solutions by attaching custom data on incoming requests, e.g:
public override void Configure(Container container)
{
RegisterTypedRequestFilter<Resource>((req, res, dto) =>
{
var route = req.GetRoute();
if (route != null && route.Path == "/tenant/{TenantName}/resource")
{
dto.SubResourceName = "CustomResource";
}
});
}
Autowired Typed Request Filters​
You can also register Autowired Typed Request and Response Filters which lets you handle Request DTOs in a Typed Filter similar to how Autowired Services handles Typed Request DTOs with access to IOC injected dependencies.
Autowired Typed Filters just needs to implement ITypedFilter<TRequest>
and can be registered in the IOC like a regular dependency, e.g:
container.RegisterAutoWiredAs<Dependency, IDependency>();
container.RegisterAutoWired<TypedRequestFilter>();
Then can be registered using the new RegisterTypedRequestFilter
overload:
this.RegisterTypedRequestFilter(c => c.Resolve<TypedRequestFilter>());
Which invokes the Typed Request Filter on each MyRequest
where it's able to access any IOC injected dependencies, e.g:
class TypedRequestFilter : ITypedFilter<MyRequest>
{
public IDependency Dependency { get; set; } // injected by IOC
public void Invoke(IRequest req, IResponse res, MyRequest dto)
{
// Handle MyRequest using a Request Filter
if (!Dependency.GrantAccess(dto, req))
{
res.StatusCode = (int)HttpStatusCode.Forbidden;
res.StatusDescription = "Thou shall not pass";
res.EndRequest();
}
}
}
Apply custom behavior to multiple DTO's with Interfaces​
Typed Filters can also be used to apply custom behavior on Request DTO's sharing a common interface, e.g:
public override void Configure(Container container)
{
RegisterTypedRequestFilter<IHasSharedProperty>((req, res, dtoInterface) => {
dtoInterface.SharedProperty = "Is Shared";
});
}
Message Queue Endpoints​
Non-HTTP requests like Redis MQ are treated as Internal Requests which only execute the alternate GlobalMessageRequestFilters
and GlobalMessageResponseFilters
and Action Filter attributes.