I\'ve got a service setup using the CorsFeature, and am using the approach that mythz suggested in other answers, collected in a function used in the appHost file:
The PreRequestFilters is only fired for valid routes which doesn't exclude OPTIONS (e.g. by leaving Verbs=null
and allow it to handle all Verbs instead - inc. OPTIONS).
To be able to handle all OPTIONS requests (i.e. even for non-matching routes) you would need to handle the Request at the start of the Request pipeline (i.e. before Routes are matched) with Config.RawHttpHandlers
. This is done in the CorsFeature for you in the next major (v4) release of ServiceStack with:
//Handles Request and closes Response after emitting global HTTP Headers
var emitGlobalHeadersHandler = new CustomActionHandler(
(httpReq, httpRes) => httpRes.EndRequest());
appHost.RawHttpHandlers.Add(httpReq =>
httpReq.HttpMethod == HttpMethods.Options
? emitGlobalHeadersHandler
: null);
CustomActionHandler doesn't exist in v3, but it's easily created with:
public class CustomActionHandler : IServiceStackHttpHandler, IHttpHandler
{
public Action<IHttpRequest, IHttpResponse> Action { get; set; }
public CustomActionHandler(Action<IHttpRequest, IHttpResponse> action)
{
if (action == null)
throw new Exception("Action was not supplied to ActionHandler");
Action = action;
}
public void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
{
Action(httpReq, httpRes);
}
public void ProcessRequest(HttpContext context)
{
ProcessRequest(context.Request.ToRequest(GetType().Name),
context.Response.ToResponse(),
GetType().Name);
}
public bool IsReusable
{
get { return false; }
}
}
Another way to match all Routes is to specify a FallbackRoute, e.g to handle all routes you can add a wildcard to the Fallback route with:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
}
But as it matches all un-handled routes it no longer gives 404 for non-matching requests since all un-matched routes are now matched. But you can easily handle it manually with:
public class FallbackService : Service
{
public object Any(Fallback request)
{
if (base.Request.HttpMethod == "OPTIONS")
return null;
throw HttpError.NotFound("{0} was not found".Fmt(request.Path));
}
}
Following steps worked for me within ServiceStackV3.
1. Added a new class CustomActionHandler
using ServiceStack.ServiceHost;
using ServiceStack.WebHost.Endpoints.Extensions;
using ServiceStack.WebHost.Endpoints.Support;
public class CustomActionHandler : IServiceStackHttpHandler, IHttpHandler
{
public Action<IHttpRequest, IHttpResponse> Action { get; set; }
public CustomActionHandler(Action<IHttpRequest, IHttpResponse> action)
{
if (action == null)
throw new Exception("Action was not supplied to ActionHandler");
Action = action;
}
public void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
{
Action(httpReq, httpRes);
}
public void ProcessRequest(HttpContext context)
{
ProcessRequest(context.Request.ToRequest(GetType().Name),
context.Response.ToResponse(),
GetType().Name);
}
public bool IsReusable
{
get { return false; }
}
}
2. Add the CustomHandler within AppHostBase.Config.RawHttpHandlers collection (this statements can be written inside the Configure(Container container) method).
// Handles Request and closes Response after emitting global HTTP Headers
var emitGlobalHeadersHandler = new CustomActionHandler((httpReq, httpRes) => httpRes.EndRequest());
Config.RawHttpHandlers.Add(httpReq => httpReq.HttpMethod == HttpMethods.Options ? emitGlobalHeadersHandler : null);
You don't have to add the OPTIONS
verb to all routes. Instead you can do the following:
Just put this route on your Question
class:
[Route("/question/{*}", Verbs = "OPTIONS")]
public class Question
{
}
Then add this to your question services class:
public void Options(Question question)
{
}
Now any route starting with /question/
will support the OPTIONS
verb.
You might want to restrict this if you're going to have subroutes like /question/something/
though.