Servicestack Rest API and Cors

ServiceStack REST API and CORS


Using the CorsFeature plugin

Enabling Global CORS support

We now have a CorsFeature which wraps CORS headers into the Plugin below to make it much easier to add CORS support to your ServiceStack services.

Commonly this is now all that's needed:

Plugins.Add(new CorsFeature());

Which uses the default values:

CorsFeature(allowedOrigins:"*", 
allowedMethods:"GET, POST, PUT, DELETE, OPTIONS",
allowedHeaders:"Content-Type",
allowCredentials:false);

You can leave out any of the values matching the default. E.g. if you just wanted to restrict the allowed methods to just GET and POST requests, you can just do:

Plugins.Add(CorsFeature(allowedMethods:"GET, POST"));

Globally enable CORS for all OPTION requests

Once the CorsFeature (or manual Global Headers) is registered, you can optionally choose to enable CORS for all OPTION requests by adding a PreRequest filter to emit all registered Global Headers (i.e. the Headers in CorsFeature) and short-circuit all OPTIONS requests with:

this.PreRequestFilters.Add((httpReq, httpRes) => {
//Handles Request and closes Responses after emitting global HTTP Headers
if (httpReq.Method == "OPTIONS")
httpRes.EndRequest(); //add a 'using ServiceStack;'
});

Enabling CORS per-service support

Instead of using the plugin above, ServiceStack also allows you to enable CORS on a per-service basis by using [EnableCors] Response Filter attribute which has the same defaults as above. E.g. You can enable just GET, POST as above with:

[EnableCors(allowedMethods:"GET,POST")]
public class MyService : Service { ... }

Manually enabling CORS

The beauty of ServiceStack is that it's built on a highly flexible and simple core. We don't try to build strong-typed APIs over everything, as it's impossible to predict what new HTTP Headers / StatusCodes will exist in the future. So whilst we provide convenient behavior to accomplish common tasks, we also provide a flexible API that lets you configure any desired HTTP Output.

Setting Global HTTP Headers

This is how to globally enable Cross Origin Sharing in you AppHost config:

public override void Configure(Container container)
{
//Permit modern browsers (e.g. Firefox) to allow sending of any REST HTTP Method
base.SetConfig(new EndpointHostConfig
{
GlobalResponseHeaders = {
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" },
{ "Access-Control-Allow-Headers", "Content-Type" },
},
});
}

Returning Custom HTTP Headers in a service

These headers will get sent on every request, alternatively you can also enable it for specific web services, i.e. take the Hello World Web Service for example:

public class Hello {
public string Name { get; set; }
}

public class HelloResponse {
public string Result { get; set; }
}

public class HelloService : IService
{
public object Any(Hello request)
{
var dto = new HelloResponse { Result = "Hello, " + request.Name };
return new HttpResult(dto) {
Headers = {
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" }
{ "Access-Control-Allow-Headers", "Content-Type" }, }
};
}
}

The above is all the C# code you need to develop a web service which is then automatically wired up for you on all HTTP Verbs (GET, POST, etc) and built-in endpoints, i.e. JSON, XML, JSV, HTML, CSV, SOAP 1.1/1.2 - for free, without any config or friction required. Checkout the live example of the above web service.

In addition to the above endpoints each service is available to be called by JSONP (another popular way to enable cross-domain service calls in Ajax apps) where each service can be called via JSONP by simply adding the ?callback=cb parameter to the querystring, e.g:

http://www.servicestack.net/ServiceStack.Hello/servicestack/hello/world?callback=cb

This is another example of the flexibility and productivity wins of using ServiceStack where you're literally given friction-free flexibility and expressive freedom in your web service to literally return just about anything and it gets serialized as expected.

It's not only easier to use than WCF (with more features out-of-the-box) but it's also much faster where all its components are highly optimized for maximum performance.

ServiceStack Cors

You do have to use:

httpRes.EndRequest();

It's important, it's an extension method in the ServiceStack namespace. I recommend using Tools like ReSharper which makes auto-finding and referencing extension methods trivial.

ServiceStack CORS Feature

Because ServiceStack's Old API enforced an interface-based API it only supported GET, POST, PUT, DELETE, PATCH requests. Handling OPTION requests we're essentially a stop-gap work-around that had a single implementation to just emit the configured headers and close the response.

With ServiceStack's New API there is no longer any limitation as you can now handle any HTTP Verb by just using its name on your IService. This now lets you handle all verbs to specific requests individually. But now it's no longer handled implicitly for you and need an implementation to handle it via a Service.

You can continue to handle all OPTIONS requests by using any of the pre-defined hooks to handle it generically before it reaches the Service.

E.g.

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

this.RequestFilters.Add((httpReq, httpRes, requestDto) => {
//Handles Request and closes Responses after emitting global HTTP Headers
if (httpReq.HttpMethod == "OPTIONS")
httpRes.EndRequest();
});

ServiceStack Javascript/Typescript client and CORS

The issue ended up being on the JsonServiceClient. I had to set the credentials to be omitted.

client.credentials="omit"

Why isn't ServiceStack adding the Access-Control-Allow-Origin header to the GET request?

ServiceStack's CorsFeature already handles OPTIONS requests so you need to remove this so it doesn't short-circuit the request.

//Should be removed
PreRequestFilters.Add((request,response) => {
if (request.Verb == "OPTIONS" )
{
response.EndRequest();
}
});

WhiteList added to Custom Http Handlers

I've added support for Origin White lists in Custom Http Handlers like /operations/metadata in this commit. This now works as expected as seen in in http://test.servicestack.net (source code):

HTTP Request

GET http://test.servicestack.net/operations/metadata HTTP/1.1
Host: test.servicestack.net
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Origin: http://localhost:8080

HTTP Response

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html
Vary: Accept
Server: Microsoft-IIS/8.5
Access-Control-Allow-Origin: http://localhost:8080
X-Powered-By: ServiceStack/4.00 Win32NT/.NET
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Allow, Authorization
Access-Control-Allow-Credentials: true
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Sun, 21 Dec 2014 00:30:39 GMT
Content-Length: 17027

This change is available from v4.0.35+ that's now available on MyGet.

ServiceStack Todo Rest Api with Crud operations - How to override Get, Post.. etc methods?

The v6 Release Notes has TODO MVC examples for all its jamstacks.net templates whose C# & UI source code is linked on each of its pages:

  • https://vue-ssg.jamstacks.net/todomvc
  • https://blazor-wasm.jamstacks.net/todomvc
  • https://nextjs.jamstacks.net/todomvc

It makes use of the new InMemory AutoQuery PocoDataSource which lets you query each property with AutoQuery's Implicit Conventions:

  • /api/QueryTodos?Id>=1
  • /api/QueryTodos?TextContains=Blazor
  • /api/QueryTodos?IsFinished=false

If your AutoQuery API Data Model had a DateTo property then you would be able to perform a "Greater than or equal to" query with any of:

 - /todos?DateTo>=2022-01-01
- /todos?DateToOnOrAfter=2022-01-01
- /todos?DateToFrom=2022-01-01
- /todos?FromDateTo=2022-01-01
- /todos?DateToSince=2022-01-01
- /todos?DateToGreaterThanOrEqualTo=2022-01-01

etc.

ServiceStack returns 405 on OPTIONS request

Not sure whether this is the right way to go, but I'm now handling the CORS myself using a request filter:

RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
httpRes.AddHeader("Access-Control-Allow-Origin", "*");

if (httpReq.HttpMethod == "OPTIONS")
{
httpRes.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
httpRes.AddHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type");
httpRes.End();
}
});


Related Topics



Leave a reply



Submit