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
Can a C# Class Inherit Attributes from Its Interface
"Invalid JSON Primitive" in Ajax Processing
Correct, Idiomatic Way to Use Custom Editor Templates with Ienumerable Models in ASP.NET MVC
What Does {0} Mean When Found in a String in C#
How to Determine a Mapped Drive's Actual Path
How to Build a Datatemplate in C# Code
Why Should I Use Int Instead of a Byte or Short in C#
Listen to Changes of Dependency Property
Capture Screenshot Including Semitransparent Windows in .Net
Servicestack Rest API and Cors
How to Loop Through Ienumerable in Batches
What Is the Equivalent of Memset in C#
Convert JSON String to JSON Object C#
Read Post Data Submitted to ASP.NET Form