Does anyone know if the ServiceStack framework can be used to create CORS REST services?
I've been banging my head against the WCF REST stuff for days now - utterly useless.
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.
Just FYI, as I had a hard time figuring out where the CORS plugin lived. Maybe I'm just thick.
It's in ServiceStack.ServiceInterface.Cors.
Related
In order to implement response caching, all that is needed to be done was:
inject .AddResponseCaching() into the services,
decorate a controller action with [ResponseCache(Duration = 10)].
Now I'm trying the minimal APIs that come with .NET 6 and I haven't figured out a way to do that except add the header myself cache-control: public,max-age=10.
Is there a more elegant way to do so?
ResponseCacheAttribute is part of MVC and as per docs will be applied to:
Razor Pages: Attributes can't be applied to handler methods.
MVC controllers.
MVC action methods: Method-level attributes override the settings specified in class-level attributes.
So it seems that adding cache header yourself is an only option. If you want you can "prettify" it a little bit using custom middleware. Something along this lines:
class CacheResponseMetadata
{
// add configuration properties if needed
}
class AddCacheHeadersMiddleware
{
private readonly RequestDelegate _next;
public AddCacheHeadersMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.GetEndpoint()?.Metadata.GetMetadata<CacheResponseMetadata>() is { } mutateResponseMetadata)
{
if (httpContext.Response.HasStarted)
{
throw new InvalidOperationException("Can't mutate response after headers have been sent to client.");
}
httpContext.Response.Headers.CacheControl = new[] { "public", "max-age=100" };
}
await _next(httpContext);
}
}
And usage:
app.UseMiddleware<AddCacheHeadersMiddleware>(); // optionally move to extension method
app.MapGet("/cache", () => $"Hello World, {Guid.NewGuid()}!")
.WithMetadata(new CacheResponseMetadata()); // optionally move to extension method
.NET 7 update
For .NET 7 you can configure output caching (see the linked article for differences with response caching):
The output caching middleware can be used in all types of ASP.NET Core apps: Minimal API, Web API with controllers, MVC, and Razor Pages.
Output caching can be done per endpoint via OutputCacheAttribute or CacheOutput method call:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) => Gravatar.WriteGravatar(context));
Or for multiple endpoints via policy:
The following code configures caching for all of the app's endpoints, with expiration time of 10 seconds.
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromSeconds(10)));
});
Disclaimer: I have not worked with SOAP web services ever. At all. Not even a little bit. So the concept of channels and WCF scaffolding has got me a bit confused, hence I'm here.
I have been asked to integrate to a SOAP XML web service which uses basic authentication. (e.g. Authorization header, Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxx <- which is a Base64 encoded username:password). My project is in .NET Core using C#.
I have used Visual Studio WCF connected service discovery to produce scaffolding code which has served me very well for instantiating the required objects etc, however my issue is I've been asked to use Basic authentication, and I have no idea where to inject this code into the scaffolding that's been produced. I have worked with basic authentication before, so I understand 'how' to do it, for things like REST APIs etc. Just username:password, base64 encode and add to Authorization header. However, I am unsure how to do this for this scaffolded SOAP web service.
The code that i believe can be injected into every request, to add your custom headers, is:
using (OperationContextScope scope = new OperationContextScope(IContextChannel or OperationContext)
{
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = new HttpRequestMessageProperty()
{
Headers =
{
{ "MyCustomHeader", Environment.UserName },
{ HttpRequestHeader.UserAgent, "My Custom Agent"}
}
};
// perform proxy operations...
}
The OperationContextScope expects either an IContextChannel or OperationContext. I am stuck as to what to add here. If I look at my scaffolded code, I can find the 'client' for the web service, here:
public partial class POSUserInformationManagerV1_2Client : System.ServiceModel.ClientBase<McaEndpointPosUserInformation.POSUserInformationManagerV1_2>, McaEndpointPosUserInformation.POSUserInformationManagerV1_2
And I can find the 'channel' here, but it's just another interface, that doesn't have any contracts specified?
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.2")]
public interface POSUserInformationManagerV1_2Channel : McaEndpointPosUserInformation.POSUserInformationManagerV1_2, System.ServiceModel.IClientChannel
{
}
I looked up ChannelBase, and it seems like it should accept a variety of objects that implement one or another channel interface (including IClientChannel, which the scaffolded POSUserInformationManagerV1_2Channel uses)
protected class ChannelBase<T> : IDisposable, IChannel, ICommunicationObject, IOutputChannel, IRequestChannel, IClientChannel, IContextChannel, IExtensibleObject<IContextChannel> where T : class
{
protected ChannelBase(ClientBase<T> client);
[SecuritySafeCritical]
protected IAsyncResult BeginInvoke(string methodName, object[] args, AsyncCallback callback, object state);
[SecuritySafeCritical]
protected object EndInvoke(string methodName, object[] args, IAsyncResult result);
But I'm still stuck on what I can put into the OperationContextScope to connect it appropriately to the 'channel'. I've tried POSUserInformationManagerV1_2Client and the relevent Channel interface, but neither will convert to an IContextChannel. Does anyone have any ideas/thoughts?
EDIT: Here is where I am trying to inject the code to add the Auth HTTP header:
public System.Threading.Tasks.Task<McaEndpointPosUserInformation.requestUserInformationResponse> requestUserInformationAsync(McaEndpointPosUserInformation.requestUserInformation request)
{
using (OperationContextScope scope = new OperationContextScope(request)
{
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = new HttpRequestMessageProperty()
{
Headers =
{
{ "MyCustomHeader", Environment.UserName },
{ HttpRequestHeader.UserAgent, "My Custom Agent"}
}
};
// perform proxy operations...
}
return base.Channel.requestUserInformationAsync(request);
}
The issue turned out to be not setting up the transport security to be 'Basic' through the use of:
// Set the binding. Without this, the WCF call will be made as anonymous
var binding = new BasicHttpsBinding();
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
Is there a way to expose Hangfire in IIS without having to configure authorization?
In this specific case the dashboard should be open, but when accessing it (not in debug) it returns a 401 code.
I think you should be able to write a custom implementation of IDashboardAuthorizationFilter as described in the documentation. Be aware that the default is only local requests to the dashboard are allowed. It's also recommended that you really use authorization and do not publish unauthorized dashboards as it contains sensitive information.
If you still want to do it, try:
Custom DashboardAuthorizationFilter
public class MyAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
return true;
}
}
Use it in the configuration of hangfire
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new [] { new MyAuthorizationFilter() }
});
I'm working with two or more ASP.NET Web APIs. One of them sends a request to the other and gets a response to be processed. It is a chain of response-request calls.
There is a recommended answer that suggests to create an ExceptionFilterAttribute to handle the exceptions in Web API and send a response with some properties data about the error:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new HandleApiExceptionAttribute());
// ...
}
}
public class HandleApiExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var request = context.ActionContext.Request;
var response = new
{
//Properties go here...
};
context.Response = request.CreateResponse(HttpStatusCode.BadRequest, response);
}
}
In this case, the client gets the response and handles the exception.
In the context where I'll have two or more Web APIs that communicate between them, if a business rule exception arises (for example invalid parameters, invalid data, no results found) I'll reply a json response with my error message.
What is the best approach to handle this error propagation between two or more web APIs?
My first approach would be to have a set of exceptions (custom exception class), so JSON response will be parsed into this class and will be switched in final client to display a friendly message to user. In this case, the JSON response will only be propagated between all the web APIs. All web APIs and final client will share a common exceptions library to identify the exception type.
Send and propagate only "string" messages between Web APIs and final client switch/match these strings to respective methods to show friendly messages for final user. It seems this will cause duplicate exception code in each web api and isn't a clean solution.
Another clean and best practice to share Business Rule JSON exceptions between Web APIs to avoid duplicity and string message dependency ?
We have an ASP.NET Web Application wired up with ServiceStack. I've never written functional tests before, but have been tasked to write tests (nUnit) against our API and prove it's working all the way down to the Database level.
Can someone help me get started writing these tests?
Here's an example of a post method on our Users service.
public object Post( UserRequest request )
{
var response = new UserResponse { User = _userService.Save( request ) };
return new HttpResult( response )
{
StatusCode = HttpStatusCode.Created,
Headers = { { HttpHeaders.Location, base.Request.AbsoluteUri.CombineWith( response.User.Id.ToString () ) } }
};
}
Now I know how to write a standard Unit Test, but am confused on this part. Do I have to call the WebAPI via HTTP and initialize a Post? Do I just call the method like I would a unit test? I suppose it's the "Functional Test" part that eludes me.
Testing the service contract
For an end-to-end functional test, I focus on verifying that the service can accept a request message and produce the expected response message for simple use cases.
A web service is a contract: given a message of a certain form, the service will produce a response message of a given form. And secondarialy, the service will alter the state of its underlying system in a certain way. Note that to the end client, the message is not your DTO class, but a specific example of a request in a given text format (JSON, XML, etc.), sent with a specific verb to a specific URL, with a given set of headers.
There are multiple layers to a ServiceStack web service:
client -> message -> web server -> ServiceStack host -> service class -> business logic
Simple unit testing and integration testing is best for the business logic layer. It's usually easy write unit tests directly against your service classes too: it should be easy to construct a DTO object, call a Get/Post method on your service class, and validate the response object. But these do not test anything that's happening inside the ServiceStack host: routing, serialization/deserialization, execution of request filters, etc. Of course, you don't want to test the ServiceStack code itself as that's framework code that has its own unit tests. But there is an opportunity to test the specific path that a specific request message takes going into the service and coming out of it. This is the part of the service contract that can't be fully verified by looking directly at the service class.
Don't try for 100% coverage
I would not recommend trying to get 100% coverage of all business logic with these functional tests. I focus on covering the major use cases with these tests - one or two reqest examples per endpoint usually. Detailed testing of specific business logic cases is much more efficiently done by writing traditional unit tests against your business logic classes. (Your business logic and data access are not implemented in your ServiceStack service classes, right?)
The implementation
We are going to run a ServiceStack service in-process and use an HTTP client to send requests to it and then verify the content of the responses. This implementation is specific to NUnit; a similar implementation should be possible in other frameworks.
First, you need an NUnit setup fixture that runs one before all of your tests, to set up the in-process ServiceStack host:
// this needs to be in the root namespace of your functional tests
public class ServiceStackTestHostContext
{
[TestFixtureSetUp] // this method will run once before all other unit tests
public void OnTestFixtureSetUp()
{
AppHost = new ServiceTestAppHost();
AppHost.Init();
AppHost.Start(ServiceTestAppHost.BaseUrl);
// do any other setup. I have some code here to initialize a database context, etc.
}
[TestFixtureTearDown] // runs once after all other unit tests
public void OnTestFixtureTearDown()
{
AppHost.Dispose();
}
}
Your actual ServiceStack implementation probably has an AppHost class that's a subclass of AppHostBase (at least if it's running in IIS). We need to subclass a different base class to run this ServiceStack host in-process:
// the main detail is that this uses a different base class
public class ServiceTestAppHost : AppHostHttpListenerBase
{
public const string BaseUrl = "http://localhost:8082/";
public override void Configure(Container container)
{
// Add some request/response filters to set up the correct database
// connection for the integration test database (may not be necessary
// depending on your implementation)
RequestFilters.Add((httpRequest, httpResponse, requestDto) =>
{
var dbContext = MakeSomeDatabaseContext();
httpRequest.Items["DatabaseIntegrationTestContext"] = dbContext;
});
ResponseFilters.Add((httpRequest, httpResponse, responseDto) =>
{
var dbContext = httpRequest.Items["DatabaseIntegrationTestContext"] as DbContext;
if (dbContext != null) {
dbContext.Dispose();
httpRequest.Items.Remove("DatabaseIntegrationTestContext");
}
});
// now include any configuration you want to share between this
// and your regular AppHost, e.g. IoC setup, EndpointHostConfig,
// JsConfig setup, adding Plugins, etc.
SharedAppHost.Configure(container);
}
}
Now you should have an in-process ServiceStack service running for all of your tests. Sending requests to this service is pretty easy now:
[Test]
public void MyTest()
{
// first do any necessary database setup. Or you could have a
// test be a whole end-to-end use case where you do Post/Put
// requests to create a resource, Get requests to query the
// resource, and Delete request to delete it.
// I use RestSharp as a way to test the request/response
// a little more independently from the ServiceStack framework.
// Alternatively you could a ServiceStack client like JsonServiceClient.
var client = new RestClient(ServiceTestAppHost.BaseUrl);
client.Authenticator = new HttpBasicAuthenticator(NUnitTestLoginName, NUnitTestLoginPassword);
var request = new RestRequest...
var response = client.Execute<ResponseClass>(request);
// do assertions on the response object now
}
Note that you may have to run Visual Studio in admin mode in order to get the service to successfully open that port; see comments below and this follow-up question.
Going further: schema validation
I work on an API for an enterprise system, where clients pay a lot of money for custom solutions and expect a highly robust service. Thus we use schema validation to be absolutely sure we don't break the service contract at the lowest level. I don't think schema validation is necessary for most projects, but here's what you can do if you want to take your testing a step further.
One of the ways in which you can inadventently break your service's contract is to change a DTO in a way that is not backward compatible: e.g., rename an existing property or alter custom serialization code. This can break a client of your service by making data no longer available or parseable, but you typically can't detect this change by unit testing your business logic. The best way to prevent this from happening is to keep your request DTOs separate and single-purpose and separate from your business/data access layer, but there's still a chance someone will accidentally apply a refactoring incorrectly.
To guard against this, you can add schema validation to your functional test. We do this only for specific use cases that we know a paying client is actually going to use in production. The idea is that if this test breaks, then we know that the code that broke the test would break this client's integration if it were to be deployed to production.
[Test(Description = "Ticket # where you implemented the use case the client is paying for")]
public void MySchemaValidationTest()
{
// Send a raw request with a hard-coded URL and request body.
// Use a non-ServiceStack client for this.
var request = new RestRequest("/service/endpoint/url", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddBody(requestBodyObject);
var response = Client.Execute(request);
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
RestSchemaValidator.ValidateResponse("ExpectedResponse.json", response.Content);
}
To validate the response, create a JSON Schema file that describes the expected format of the response: what fields are are required to exist for this specific use case, what data types are expected, etc. This implementation uses the Json.NET schema parser.
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
public static class RestSchemaValidator
{
static readonly string ResourceLocation = typeof(RestSchemaValidator).Namespace;
public static void ValidateResponse(string resourceFileName, string restResponseContent)
{
var resourceFullName = "{0}.{1}".FormatUsing(ResourceLocation, resourceFileName);
JsonSchema schema;
// the json file name that is given to this method is stored as a
// resource file inside the test project (BuildAction = Embedded Resource)
using(var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFullName))
using(var reader = new StreamReader(stream))
using (Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFileName))
{
var schematext = reader.ReadToEnd();
schema = JsonSchema.Parse(schematext);
}
var parsedResponse = JObject.Parse(restResponseContent);
Assert.DoesNotThrow(() => parsedResponse.Validate(schema));
}
}
Here's an example of a json schema file. Note that this is specific to this one use case and is not a generic description of the response DTO class. The properties are all marked as required as these are the specific ones the client are expecting in this use case. The schema might leave out other unused properties that currently exist in the response DTO. Based on this schema, the call to RestSchemaValidator.ValidateResponse will fail if any of the expected fields are missing in the response JSON, have unexpected data types, etc.
{
"description": "Description of the use case",
"type": "object",
"additionalProperties": false,
"properties":
{
"SomeIntegerField": {"type": "integer", "required": true},
"SomeArrayField": {
"type": "array",
"required": true,
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"Property1": {"type": "integer", "required": true},
"Property2": {"type": "string", "required": true}
}
}
}
}
}
This type of test should be written once and never modified unless the use case it's modeled on becomes obsolete. The idea is that these tests will represent actual usages of your API in production and ensure that the exact messages your API promises to return do not change in a way that breaks existing usages.
Other info
ServiceStack itself has some examples of running tests against an in-process host, on which the above implementation is based.