using System; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; public class MessageInspector : IDispatchMessageInspector { private ServiceEndpoint _serviceEndpoint; public MessageInspector(ServiceEndpoint serviceEndpoint) { _serviceEndpoint = serviceEndpoint; } /// /// Called when an inbound message been received /// /// The request message. /// The incoming channel. /// The current service instance. /// /// The object used to correlate stateMsg. /// This object is passed back in the method. /// public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { StateMessage stateMsg = null; HttpRequestMessageProperty requestProperty = null; if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name)) { requestProperty = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; } if (requestProperty != null) { var origin = requestProperty.Headers["Origin"]; if (!string.IsNullOrEmpty(origin)) { stateMsg = new StateMessage(); // if a cors options request (preflight) is detected, // we create our own reply message and don't invoke any // operation at all. if (requestProperty.Method == "OPTIONS") { stateMsg.Message = Message.CreateMessage(request.Version, null); } request.Properties.Add("CrossOriginResourceSharingState", stateMsg); } } return stateMsg; } /// /// Called after the operation has returned but before the reply message /// is sent. /// /// The reply message. This value is null if the /// operation is one way. /// The correlation object returned from /// the method. public void BeforeSendReply(ref Message reply, object correlationState) { var stateMsg = correlationState as StateMessage; if (stateMsg != null) { if (stateMsg.Message != null) { reply = stateMsg.Message; } HttpResponseMessageProperty responseProperty = null; if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name)) { responseProperty = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty; } if (responseProperty == null) { responseProperty = new HttpResponseMessageProperty(); reply.Properties.Add(HttpResponseMessageProperty.Name, responseProperty); } // Access-Control-Allow-Origin should be added for all cors responses responseProperty.Headers.Set("Access-Control-Allow-Origin", "*"); if (stateMsg.Message != null) { // the following headers should only be added for OPTIONS requests responseProperty.Headers.Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET"); responseProperty.Headers.Set("Content-Type", "application/json"); responseProperty.Headers.Set("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization, x-requested-with"); } } } } class StateMessage { public Message Message; } public class BehaviorAttribute : Attribute, IEndpointBehavior, IOperationBehavior { public void Validate(ServiceEndpoint endpoint) { } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } /// /// This service modify or extend the service across an endpoint. /// /// The endpoint that exposes the contract. /// The endpoint dispatcher to be /// modified or extended. public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // add inspector which detects cross origin requests endpointDispatcher.DispatchRuntime.MessageInspectors.Add( new MessageInspector(endpoint)); } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void Validate(OperationDescription operationDescription) { } public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { } public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } }