2

Lets say I have a route like this:

[Route("/users/{Id}", "DELETE")]
public class DeleteUser
{
    public Guid Id { get; set; }
}

If I am using CORS with a custom header, an OPTIONS preflight request will be sent out. This will happen on all requests. With the above route, the route will fire, but the OPTIONS will 404 and the ajax error handler will fire.

I could modify the route to be [Route("/users/{Id}", "DELETE OPTIONS")] but I would need to do this on every route I have. Is there a way to globally allow OPTIONS for all custom routes?

Edit

Since it looks like this behavior is incorrect when a RequestFilter allows for OPTIONS, I am temporarily using an Subclassed Attribute that just automatically adds OPTIONS to the verbs

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ServiceRoute : RouteAttribute
{
    public ServiceRoute(string path) : base(path) {}
    public ServiceRoute(string path, string verbs) 
           : base(path, string.Format("{0} OPTIONS", verbs)) {}
}
Kyeotic
  • 19,697
  • 10
  • 71
  • 128

1 Answers1

1

As seen in this earlier answer, you can add globally enable CORS for all options request by adding the CorsFeature plugin:

Plugins.Add(new CorsFeature()); //Registers global CORS Headers

If you then want to, you can simply add a PreRequest filter to emit all Global Headers (e.g. registered in CorsFeature) and short-circuit all OPTIONS requests with:

this.RequestFilters.Add((httpReq, httpRes, requestDto) => {
   //Handles Request and closes Responses after emitting global HTTP Headers
    if (httpReq.Method == "OPTIONS") 
        httpRes.EndServiceStackRequest();
});
Community
  • 1
  • 1
mythz
  • 141,670
  • 29
  • 246
  • 390
  • So I put this in, but it doesn't work. The problem is that if the route specifies a Verb (Like `GET`), that filter is never hit for OPTIONS, only for GET. If I add OPTIONS to the Route Verbs, that filter is hit. The Route verbs seem to take precedence to even `PreRequestFilters`. – Kyeotic May 03 '13 at 20:15
  • 1
    ok weird - will check it out, well it should definitely work for normal RequestFilters, just updated answer to use RequestFilters. – mythz May 03 '13 at 20:19
  • I had it as `RequestFilters` before, they both do the same thing. Keep in mind for testing that an OPTIONS request only goes out when you have a custom header. It works fine without the custom header. – Kyeotic May 03 '13 at 20:30
  • 1
    I put both `PreRequestFilters` and `RequestFilters` in, Pre definitely gets hit first, but they still both only get hit if OPTIONS is already a route verb. – Kyeotic May 03 '13 at 20:33
  • Right it still needs to match a valid route, tho you can also register it in `AppHost.CatchAllHandlers` instead, which gets called as a fallback for non-matching routes - you can use it hi-jack a HTTP Request by detecting and returning a custom `IHttpHandler`. – mythz May 03 '13 at 20:41
  • Yeah, I get why it needs to match a valid route, but I was hoping there was a way to make all routes allow OPTIONS without having to actually put OPTIONS *in all of them*. if `AppHost.CatchAllHandlers` catches even non-existent routes, then invalid routes will return OPTIONS 200, which is bad. It looks like there isn't based on what you are saying. – Kyeotic May 03 '13 at 20:45
  • I really can't see the problem: you've got the pre-request/request filters getting called on any **matching routes** and Config.RawHttpHandlers being called for **all routes** and Config.CatchAllHandlers getting called as a for fallback for non-handled requests. They each give you the full freedom to provide your own custom C# logic, so you should be able to do anything you want to, in all cases. – mythz May 05 '13 at 08:00
  • The problem is that a route that I specify as "PUT /hdo" does not have a valid route for "OPTIONS /hdo". Which means in the case that I want to respond to OPTIONS request *only for otherwise valid routes* ("/hdo", but not "/lol", there is no "/lol" route) I don't have a handler I can use. Actually adding "OPTIONS" to every single route seems very non DRY. My custom route attribute works, I was just wondering if SS already had something for this case. – Kyeotic May 05 '13 at 16:34
  • When you specify `PUT /hdo` then as expected `OPTIONS /hdo` is a non-matching route. You can use raw or catch-all handlers to handle OPTION requests on non-matching routes. In your handlers you can look at EndpointHost.Meta.Routes to look at all defined routes and handle it differently if the PathInfo matches on Route but not on verb if you want to. You can also provide your own convention-based Routes to include OPTIONS on all of them as seen in [Routes.AddFromAssembly](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.ServiceInterface/ServiceRoutesExtensions.cs) – mythz May 05 '13 at 17:11
  • I know it becomes a non matching route when I specify another verb. Filtering a catch-all on `EndpointHost.Meta.Routes` sounds like a good idea, I could even write that as a plugin wrapping CORS. Thanks for the tip on `Routes.AddFromAssembly`, I didn't know about that one. – Kyeotic May 05 '13 at 17:21
  • After moving all my routes to be declared on DTOs. I had the same problem as Tyrsius. I agree it was confusing. I ended up creating dynamic code to go through and add all OPTIONS to routes. See the gist for how I did it in ServiceStack v3.9 https://gist.github.com/jokecamp/f8ca5ea84a1aafe1c76b – kampsj Jun 04 '14 at 15:18