WCF Custom Headers/ Custom Behaviors class not available - c#

I'm fairly new at WCF so i'm positive this is something i'm doing wrong. I was following this link Sample link
Basically i'm trying to implement 'IClientMessageInspector' so that every call to my WCF must contain custom attributes that I need for my WCF methods to perform the correct activities.
In my Console Consumer app I'm trying to add my custom endpoint behavior class. However when I go to add the class it's not available. Here is my consumer app you'll notice the line of code i'm having the issue with.
Program.cs
class Program
{
static void Main(string[] args)
{
using (Service1Client s1c = new Service1Client())
{
//cannot add 'CustomBehavior' this is my issue
s1c.ChannelFactory.Endpoint.EndpointBehaviors.Add(
}
}
}
My Service
Service1.svc.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCFHeaderCalls
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
[CustomBehavior]
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
}
IService1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCFHeaderCalls
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}
ContextClass.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace WCFHeaderCalls
{
/// <summary>
/// This class will act as a custom context in the client side to hold the context information.
/// </summary>
public class ClientContext
{
public static string applicationKey;
public static string methodKey;
public static string accountName;
public static string accountPassword;
}
/// <summary>
/// This class will act as a custom context, an extension to the OperationContext.
/// This class holds all context information for our application.
/// </summary>
public class ServerContext : IExtension<OperationContext>
{
public string EmployeeID;
public string WindowsLogonID;
public string KerberosID;
public string SiteminderToken;
// Get the current one from the extensions that are added to OperationContext.
public static ServerContext Current
{
get
{
return OperationContext.Current.Extensions.Find<ServerContext>();
}
}
#region IExtension<OperationContext> Members
public void Attach(OperationContext owner)
{
}
public void Detach(OperationContext owner)
{
}
#endregion
}
}
MessageInspector.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace WCFHeaderCalls
{
/// <summary>
/// This class is used to inspect the message and headers on the server side,
/// This class is also used to intercept the message on the
/// client side, before/after any request is made to the server.
/// </summary>
public class CustomMessageInspector : IClientMessageInspector, IDispatchMessageInspector
{
#region Message Inspector of the Service
/// <summary>
/// This method is called on the server when a request is received from the client.
/// </summary>
/// <param name="request"></param>
/// <param name="channel"></param>
/// <param name="instanceContext"></param>
/// <returns></returns>
public object AfterReceiveRequest(ref Message request,
IClientChannel channel, InstanceContext instanceContext)
{
// Create a copy of the original message so that we can mess with it.
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message messageCopy = buffer.CreateMessage();
// Read the custom context data from the headers
ServiceHeader customData = CustomHeader.ReadHeader(request);
// Add an extension to the current operation context so
// that our custom context can be easily accessed anywhere.
ServerContext customContext = new ServerContext();
if (customData != null)
{
customContext.KerberosID = customData.accountName;
customContext.SiteminderToken = customData.accountPassword;
}
OperationContext.Current.IncomingMessageProperties.Add(
"CurrentContext", customContext);
return null;
}
/// <summary>
/// This method is called after processing a method on the server side and just
/// before sending the response to the client.
/// </summary>
/// <param name="reply"></param>
/// <param name="correlationState"></param>
public void BeforeSendReply(ref Message reply, object correlationState)
{
// Do some cleanup
OperationContext.Current.Extensions.Remove(ServerContext.Current);
}
#endregion
#region Message Inspector of the Consumer
/// <summary>
/// This method will be called from the client side just before any method is called.
/// </summary>
/// <param name="request"></param>
/// <param name="channel"></param>
/// <returns></returns>
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Prepare the request message copy to be modified
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
ServiceHeader customData = new ServiceHeader();
customData.accountName = ClientContext.accountName;
customData.accountPassword = ClientContext.accountPassword;
CustomHeader header = new CustomHeader(customData);
// Add the custom header to the request.
request.Headers.Add(header);
return null;
}
/// <summary>
/// This method will be called after completion of a request to the server.
/// </summary>
/// <param name="reply"></param>
/// <param name="correlationState"></param>
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
#endregion
}
}
CustomHeader.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace WCFHeaderCalls
{
[DataContract]
public class ServiceHeader
{
/// <summary>
/// this is the application-GUID specify this per app (OI,office,etc..)
/// </summary>
[DataMember]
public string applicationKey { get; set; }
/// <summary>
/// this is the method key you wish to implament
/// </summary>
[DataMember]
public string methodKey { get; set; }
/// <summary>
/// this is your account name
/// </summary>
[DataMember]
public string accountName { get; set; }
/// <summary>
/// this is your account password
/// </summary>
[DataMember]
public string accountPassword { get; set; }
}
public class CustomHeader : MessageHeader
{
private const string CUSTOM_HEADER_NAME = "HeaderName";
private const string CUSTOM_HEADER_NAMESPACE = "YourNameSpace";
private ServiceHeader _customData;
public ServiceHeader CustomData
{
get
{
return _customData;
}
}
public CustomHeader()
{
}
public CustomHeader(ServiceHeader customData)
{
_customData = customData;
}
public override string Name
{
get { return (CUSTOM_HEADER_NAME); }
}
public override string Namespace
{
get { return (CUSTOM_HEADER_NAMESPACE); }
}
protected override void OnWriteHeaderContents(
System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion)
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceHeader));
StringWriter textWriter = new StringWriter();
serializer.Serialize(textWriter, _customData);
textWriter.Close();
string text = textWriter.ToString();
writer.WriteElementString(CUSTOM_HEADER_NAME, "Key", text.Trim());
}
public static ServiceHeader ReadHeader(Message request)
{
Int32 headerPosition = request.Headers.FindHeader(CUSTOM_HEADER_NAME, CUSTOM_HEADER_NAMESPACE);
if (headerPosition == -1)
return null;
MessageHeaderInfo headerInfo = request.Headers[headerPosition];
XmlNode[] content = request.Headers.GetHeader<XmlNode[]>(headerPosition);
string text = content[0].InnerText;
XmlSerializer deserializer = new XmlSerializer(typeof(ServiceHeader));
TextReader textReader = new StringReader(text);
ServiceHeader customData = (ServiceHeader)deserializer.Deserialize(textReader);
textReader.Close();
return customData;
}
}
}
CustomBehavior.cs
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace WCFHeaderCalls
{
/// <summary>
/// This custom behavior class is used to add both client and server inspectors to
/// the corresponding WCF end points.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class CustomBehavior : Attribute, IServiceBehavior, IEndpointBehavior
{
#region IEndpointBehavior Members
void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
CustomMessageInspector inspector = new CustomMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher ed in channelDispatcher.Endpoints)
{
CustomMessageInspector inspector = new CustomMessageInspector();
ed.DispatchRuntime.MessageInspectors.Add(inspector);
}
}
}
void IEndpointBehavior.Validate(ServiceEndpoint endpoint) { }
#endregion
#region IServiceBehavior Members
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription desc, ServiceHostBase host)
{
foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
eDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
}
void IServiceBehavior.Validate(ServiceDescription desc, ServiceHostBase host) { }
#endregion
}
}
And finally here is my services web.config
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>

The message behaviors won't be available via WSDL in generated proxy in your consumer app. So either you have to add the same code(classes) in client side for message inspector or create a shared project(class library) for contracts between service and client through which the behavior class will be available in both.

Related

Error control/handler (capture) with Behavior in wcf. Business layer

I have a project (backend) where it receives a call in the service layer, but it also makes calls to another API in the business layer.
I have added a behaviour to those calls so I do not have to make changes to several places in my code.
My Behaviour.config (the importatn is ExternalDependencyBehavior):
<?xml version="1.0" encoding="utf-8"?>
<behaviors>
<endpointBehaviors>
<!-- SPECIFIC BEHAVIOR EXTENSION FOR JSON URL DESERIALIZER-->
<behavior name="ServiceRESTBehavior">
<ConverterWebHttpBehaviourExtension />
</behavior>
<behavior name="ServiceSOAPBehavior" />
<behavior name="ExternalDependencyBehavior">
<ExternalClientBehavior/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="DefaultBehaviour">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
My code: (one method in business layer that call a other API external)
protected virtual Vendor.Sinacofi.SinacofiService.RespuestaSNPV1201 RequestToVendor(VendorWebService vendor, string clientFiscalId, BasicSearchCriterion criteria)
{
Vendor.Sinacofi.SinacofiService.RespuestaSNPV1201 result = new Vendor.Sinacofi.SinacofiService.RespuestaSNPV1201();
string errorLogProvider = null;
try
{
using (Vendor.Sinacofi.SinacofiService.SNPV1201SoapClient client = new Vendor.Sinacofi.SinacofiService.SNPV1201SoapClient())
{
/*Credentials*/
result = client.ConsultaRutificacion(
client.ClientCredentials.HttpDigest.ClientCredential.UserName,
client.ClientCredentials.HttpDigest.ClientCredential.Password,
clientFiscalId,
criteria.Value
);
}
return result;
}
catch (AxrException)
{
throw;
}
catch (FaultException fex)
{
this.logProvider.WriteInErrorMode(GetType().Name, MethodBase.GetCurrentMethod().Name, fex);
throw this.exceptionProvider.Create(ErrorCode.---------, ExceptionType.BUSINESS, fex);
}
catch (Exception ex)
{
this.logProvider.WriteInErrorMode(GetType().Name, MethodBase.GetCurrentMethod().Name, ex);
throw this.exceptionProvider.Create(ErrorCode.-------------, ExceptionType.BUSINESS, ex);
}
}
In summary:
When I arrive at the call:
"client.ConsultaRutification"
My behaviour is executed, I add the code (2 files):
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace SIPE.Search.Helpers
{
/// <summary>
/// Implements methods that can be used to extend run-time behavior for an endpoint in either a client application.
/// </summary>
public class ExternalClientBehavior : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ExternalClientBehaviorClass();
}
public override Type BehaviorType
{
get
{
return typeof(ExternalClientBehaviorClass);
}
}
/// <summary>
/// JSON REST[GET] Converter Behavior
/// </summary>
private class ExternalClientBehaviorClass : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
ExternalClientMessageInspector clientInspector = new ExternalClientMessageInspector(endpoint);
clientRuntime.MessageInspectors.Add(clientInspector);
foreach (ClientOperation op in clientRuntime.Operations)
{
op.ParameterInspectors.Add(clientInspector);
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ExternalClientMessageInspector(endpoint));
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
}
}
And file two:
namespace SIPE.Search.Helpers
{
/// <summary>
/// Intercepts send requests.
/// </summary>
public class ExternalClientMessageInspector : IClientMessageInspector, IParameterInspector, IErrorHandler
{
private ServiceEndpoint Endpoint { get; set; }
Dictionary<string, object> inputsParam;
public ExternalClientMessageInspector(ServiceEndpoint endpoint)
{
//empty
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
((Dictionary<string, string>)(System.Web.HttpContext.Current.Items[operationName])).Add("OutputParam", JsonConvert.SerializeObject(returnValue));
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
//code not necessary
}
public object BeforeCall(string operationName, object[] inputs)
{
// code not neccesary
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
Message copy = buffer.CreateMessage(); // Create a copy to work with
request = buffer.CreateMessage(); // Restore the original message
return copy.GetReaderAtBodyContents().Name;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// no implement
}
public bool HandleError(Exception error)
{
// no implement
return true;
}
public void NotifyError()
{
}
}
}
I need to be able to capture the errors, (for example 404 and others) of my calls within my behaviour.
I have tried to add several interfaces, but without success
The error is captured by the catch of the RequestToVendor method that is in the business layer.
But I need to be able to capture it in my behaviour.
thank you

Request.Headers.Authorization is always null even after using [Authorize]

I am currently working on authorizing calls to my API controllers. I am following this tutorial: http://www.c-sharpcorner.com/article/asp-net-mvc5-rest-web-api-authorization/
WebApiController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebApiAuthorization.Controllers {
[Authorize]
public class WebApiController: ApiController {
// GET api/values
public IEnumerable < string > Get() {
return new string[] {
"Hello REST API",
"I am Authorized"
};
}
// GET api/values/5
public string Get(int id) {
return "Hello Authorized API with ID = " + id;
}
// POST api/values
public void Post([FromBody] string value) {}
// PUT api/values/5
public void Put(int id, [FromBody] string value) {}
// DELETE api/values/5
public void Delete(int id) {}
}
}
AuthorizationHeaderHandler.cs
namespace WebApiAuthorization.Helper_Code.Common {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using WebApiAuthorization.Resources.Constants;
/// <summary>
/// Authorization for web API class.
/// </summary>
public class AuthorizationHeaderHandler: DelegatingHandler {#
region Send method.
/// <summary>
/// Send method.
/// </summary>
/// <param name="request">Request parameter</param>
/// <param name="cancellationToken">Cancellation token parameter</param>
/// <returns>Return HTTP response.</returns>
protected override Task < HttpResponseMessage > SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
// Initialization.
IEnumerable < string > apiKeyHeaderValues = null;
AuthenticationHeaderValue authorization = request.Headers.Authorization; //This is always null even as I put [Authorize] tag above my controller
string userName = null;
string password = null;
// Verification.
if (request.Headers.TryGetValues(ApiInfo.API_KEY_HEADER, out apiKeyHeaderValues) && !string.IsNullOrEmpty(authorization.Parameter)) {
var apiKeyHeaderValue = apiKeyHeaderValues.First();
// Get the auth token
string authToken = authorization.Parameter;
// Decode the token from BASE64
string decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
// Extract username and password from decoded token
userName = decodedToken.Substring(0, decodedToken.IndexOf(":"));
password = decodedToken.Substring(decodedToken.IndexOf(":") + 1);
// Verification.
if (apiKeyHeaderValue.Equals(ApiInfo.API_KEY_VALUE) && userName.Equals(ApiInfo.USERNAME_VALUE) && password.Equals(ApiInfo.PASSWORD_VALUE)) {
// Setting
var identity = new GenericIdentity(userName);
SetPrincipal(new GenericPrincipal(identity, null));
}
}
// Info.
return base.SendAsync(request, cancellationToken);
}#
endregion# region Set principal method.
/// <summary>
/// Set principal method.
/// </summary>
/// <param name="principal">Principal parameter</param>
private static void SetPrincipal(IPrincipal principal) {
// setting.
Thread.CurrentPrincipal = principal;
// Verification.
if (HttpContext.Current != null) {
// Setting.
HttpContext.Current.User = principal;
}
}#
endregion
}
}
The problem I am facing is in the SendAsync method:
AuthenticationHeaderValue authorization = request.Headers.Authorization; //This is always null even as I put [Authorize] tag above my controller
Doesn't matter if I put [Authorize], [AllowAnonymous], authorization is always null. Looking for some help. Thanks in advance!

Deserializing JSON in REST Service

I am having a problem deserializing json in my rest self-hosted service.
I have a test page that invokes the self-hosted REST with JSON, here is the code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript">
function doFunction() {
xhr = new XMLHttpRequest();
var url = "https://localhost:1234/business/test/testing2/endpoint";
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var json = JSON.parse(xhr.responseText);
alert(json);
}
}
var data = JSON.stringify({ testing : "test" });
xhr.send(data);
}
</script>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<input id="clickMe" type="button" value="clickme" onclick="doFunction();" />
</div>
</form>
</body>
</html>
And here is the interface and contracts of my self-hosted service:
[DataContract]
public class OperationInput
{
[DataMember]
public string testing { get; set; }
}
[DataContract]
public class OperationOutput
{
[DataMember]
public int Status { get; set; }
[DataMember]
public string Message { get; set; }
[DataMember]
public string AddInfo { get; set; }
[DataMember]
public string PartnerID { get; set; }
[DataMember]
public string SessionID { get; set; }
}
[ServiceContract]
interface IRegisterOperation
{
[OperationContract]
[WebInvoke(UriTemplate = "/endpoint",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, Method = "*")]
OperationOutput Operation(OperationInput order);
}
And here is the implementation of the interface:
public class RegisterOperation : IRegisterOperation
{
public OperationOutput Operation(OperationInput input)
{
System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\testing.txt", false);
file.WriteLine(input.testing);
file.Close();
OperationOutput output = new OperationOutput();
output.Status = 200;
output.Message = "The action has been successfully recorded on NAVe";
output.AddInfo = "";
return output;
}
}
I am creating the self-host using this code:
host = new ServiceHost(implementationType, baseAddress);
ServiceEndpoint se = host.AddServiceEndpoint(endpointType, new WebHttpBinding(WebHttpSecurityMode.Transport), "");
se.Behaviors.Add(new WebHttpBehavior());
host.Open();
Using debug I notice that it hits the breakpoint inside my service, so the call to localhost is working but the parameter of input is null, as you can see in the image below:
Here is 2 images capturing the POST Request with JSON on fiddler:
Have you any idea why I am getting null ? instead of the string "test" as I do on the invocation in javascript?
Thank you very much in advance ;)
EDIT:
I activated HTTPS Decryption on Fiddler and now pressed "Yes" to install certificate on trusted ca instead of pressin "no", and the breakpoint is hitted, and fiddler now captured an options request as the following images represent
Shouldn't this be a post request instead of a options request?? maybe that's why I don't see the json?
Thanks a lot
I figured it out, the problem was the OPTIONS Request, I need to receive a POST Request so I get the JSON.
I added a behaviour attribute to my service host so that it responds to options request allowing the wcf service host to receive cross origin requests.
So I added the code from the answer of this (Cross Origin Resource Sharing for c# WCF Restful web service hosted as Windows service) question and now I get a POST Request after the first Options request:
If the link is no longer available, here is the solution of the question:
CODE:
Create 2 classes as follows:
MessageInspector implementing IDispatchMessageInspector.
BehaviorAttribute implementing Attribute, IEndpointBehavior and IOperationBehavior.
With the following details:
//MessageInspector Class
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
namespace myCorsService
{
public class MessageInspector : IDispatchMessageInspector
{
private ServiceEndpoint _serviceEndpoint;
public MessageInspector(ServiceEndpoint serviceEndpoint)
{
_serviceEndpoint = serviceEndpoint;
}
/// <summary>
/// Called when an inbound message been received
/// </summary>
/// <param name="request">The request message.</param>
/// <param name="channel">The incoming channel.</param>
/// <param name="instanceContext">The current service instance.</param>
/// <returns>
/// The object used to correlate stateMsg.
/// This object is passed back in the method.
/// </returns>
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;
}
/// <summary>
/// Called after the operation has returned but before the reply message
/// is sent.
/// </summary>
/// <param name="reply">The reply message. This value is null if the
/// operation is one way.</param>
/// <param name="correlationState">The correlation object returned from
/// the method.</param>
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("Access-Control-Allow-Headers",
"Content-Type, Accept, Authorization, x-requested-with");
}
}
}
}
class StateMessage
{
public Message Message;
}
}
//BehaviorAttribute Class
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace OpenBetRetail.NFCReaderService
{
public class BehaviorAttribute : Attribute, IEndpointBehavior,
IOperationBehavior
{
public void Validate(ServiceEndpoint endpoint) { }
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) { }
/// <summary>
/// This service modify or extend the service across an endpoint.
/// </summary>
/// <param name="endpoint">The endpoint that exposes the contract.</param>
/// <param name="endpointDispatcher">The endpoint dispatcher to be
/// modified or extended.</param>
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) { }
}
}
After this all you need to do is add this message inspector to service end point behavior.
ServiceHost host = new ServiceHost(typeof(myService), _baseAddress);
foreach (ServiceEndpoint EP in host.Description.Endpoints)
EP.Behaviors.Add(new BehaviorAttribute());
Thanks for everyone help ;)
You have Method = "*"
I would experiment with:
Method = "POST" ....
[ServiceContract]
interface IRegisterOperation
{
OperationOutput Operation(OperationInput order);
like so:
[OperationContract]
[WebInvoke(UriTemplate = "/registeroperation",
Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare)]
OperationOutput Operation(OperationInput order);
APPEND:
Your json does not look right (from the screen shots)
I would expect something simple like:
{
"ACTPRDX": "test"
}
Can you do an "alert" right after you stringify the object?
And show the results?
But (in general)...if your json is messed up, the "auto voodoo" of the Wcf Service Method won't work.
.....
This might be nit-picky, but try this:
Note the capital "T" after the hyphen.
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
I just found this in my code:
var jsonObject = { ACTPRDX : "test" };
var whatToSendOverTheWire = JSON.stringify(jsonObject);
Try that.
As mentioned, your json is wrong. the fix is to figure out how its being screwed up.
You actually pass primitive instead of expected object
var data = JSON.stringify({ ACTPRDX : "test" });
The above data would work for method :
public XYZ Something(string ACTPRDX)
You should send object to your method like that
var obj= new Object();
obj.ACTPRDX = "test";
var data = JSON.stringify({ order: obj});
Also interface and implementation have different name for parameter OperationInput -> order and input.

SignalR resolving a hub context from a change event handler

I have an issue where I can't seem to send new data to the connected Signal R clients from a ChangedEventHandler. The docs says that I can get the hub context by using:-
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.addToList(insertedCustomer);
However nothing gets sent to the clients (checked on fiddler) or any errors reported. My onchange event is wired up at the moment from Application_Start as I am creating a proof of concept. I should point out the hub does work on start up and retrieves the data from the initial GetAll call
protected void Application_Start()
{
...
_sqlTableDependency.OnChanged += _sqlTableDependency_OnChanged;
_sqlTableDependency.Start();
...
}
private void _sqlTableDependency_OnChanged(object sender, RecordChangedEventArgs<BiddingText> e)
{
switch (e.ChangeType)
{
case ChangeType.Insert:
foreach (var insertedCustomer in e.ChangedEntities)
{
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.addToList(insertedCustomer);
biddingTextList.Add(insertedCustomer);
}
break;
}
}
When I put a breakpoint on the hub context I get my ChatHub back.
My Javascript code:
$.connection.hub.url = "http://localhost:37185/signalr";
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
chat.client.initialText = function(data) {
var index;
//console.log(data.length);
for (index = 0; index < data.List.length; ++index) {
$('#list').append("<li>" + data.List[index].text + "</li>");
}
};
chat.client.addToList = function(data) {
console.log(data);
$('#list').append("<li>" + data.text + "</li>");
};
// Start the connection.
$.connection.hub.start({ jsonp: true }).done(function () {
chat.server.getAll(1831);
});
My Hub code:
public class ChatHub : Microsoft.AspNet.SignalR.Hub
{
private readonly IMediator mediator;
public ChatHub(IMediator mediator)
{
this.mediator = mediator;
}
public void GetAll(int saleId)
{
var model = mediator.Request(new BiddingTextQuery { SaleId = saleId});
Clients.Caller.initialText(model);
}
}
Not sure if this is relevant but the Clients.Connection.Identity is different everytime I use GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
Can anyone help?
I had some similar issues a while back setting up a Nancy API to publish some events to SignalR clients.
The core issue I had was that I had failed to ensure Nancy and SignalR was using the same DI container at the SignalR global level.
SignalR, as Nancy, has a default DependencyResolver that is used to resolve any dependencies in your hubs. When I failed to implement the same source of dependencies for Nancy and SignalR I basically ended up with two separate applications.
Small disclaimer: You have not posted your config code, so my solution here is based on some assumptions (as well as the following twitter answer from David Fowler when you reached out on twitter:
#rippo you have a custom dependency resolver and global has has another. You need to use one container
(https://twitter.com/davidfowl/status/635000470340153344)
Now some code:
First you need to implement a custom SignalR depependency resolver, and ensure it uses the same source of dependencies as the rest of your application.
This is the implementation I used for an Autofac container:
using Autofac;
using Autofac.Builder;
using Autofac.Core;
using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LabCommunicator.Server.Configuration
{
internal class AutofacSignalrDependencyResolver : DefaultDependencyResolver, IRegistrationSource
{
private ILifetimeScope LifetimeScope { get; set; }
public AutofacSignalrDependencyResolver(ILifetimeScope lifetimeScope)
{
LifetimeScope = lifetimeScope;
var currentRegistrationSource = LifetimeScope.ComponentRegistry.Sources.FirstOrDefault(s => s.GetType() == GetType());
if (currentRegistrationSource != null)
{
((AutofacSignalrDependencyResolver)currentRegistrationSource).LifetimeScope = lifetimeScope;
}
else
{
LifetimeScope.ComponentRegistry.AddRegistrationSource(this);
}
}
public override object GetService(Type serviceType)
{
object result;
if (LifetimeScope == null)
{
return base.GetService(serviceType);
}
if (LifetimeScope.TryResolve(serviceType, out result))
{
return result;
}
return null;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
object result;
if (LifetimeScope == null)
{
return base.GetServices(serviceType);
}
if (LifetimeScope.TryResolve(typeof(IEnumerable<>).MakeGenericType(serviceType), out result))
{
return (IEnumerable<object>)result;
}
return Enumerable.Empty<object>();
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var typedService = service as TypedService;
if (typedService != null)
{
var instances = base.GetServices(typedService.ServiceType);
if (instances != null)
{
return instances
.Select(i => RegistrationBuilder.ForDelegate(i.GetType(), (c, p) => i).As(typedService.ServiceType)
.InstancePerLifetimeScope()
.PreserveExistingDefaults()
.CreateRegistration());
}
}
return Enumerable.Empty<IComponentRegistration>();
}
bool IRegistrationSource.IsAdapterForIndividualComponents
{
get { return false; }
}
}
}
Next, in your SignalR config, you need to assign the custom dependency resolver:
Note: My app was using owin, so you may not need the HubConfiguration bit, but you need the GlobalHost bit (which is the one I messed up when my stuff was not working).
var resolver = new AutofacSignalrDependencyResolver(container);
'Owin config options.
var config = new HubConfiguration()
{
Resolver = resolver,
EnableDetailedErrors = true,
EnableCrossDomain = true
};
GlobalHost.DependencyResolver = resolver;
'More owin stuff
app.MapHubs(config);
Hope this will help resolve your issue.
You need to keep track of the clients that connect to the hub, and then send them the new messages, something like this
This is a base class I wrote for my Hubs
/// <summary>
/// base class for Hubs in the system.
/// </summary>
public class HubBase : Hub {
/// <summary>
/// The hub users
/// </summary>
protected static ConcurrentDictionary<Guid, HubUser> Users = new ConcurrentDictionary<Guid, HubUser>();
/// <summary>
/// Called when the connection connects to this hub instance.
/// </summary>
/// <returns>
/// A <see cref="T:System.Threading.Tasks.Task" />
/// </returns>
public override System.Threading.Tasks.Task OnConnected() {
Guid userName = RetrieveUserId();
string connectionId = Context.ConnectionId;
HubUser user = Users.GetOrAdd(userName, _ => new HubUser {
UserId = userName,
ConnectionIds = new HashSet<string>()
});
lock (user.ConnectionIds) {
user.ConnectionIds.Add(connectionId);
}
return base.OnConnected();
}
/// <summary>
/// Called when a connection disconnects from this hub gracefully or due to a timeout.
/// </summary>
/// <param name="stopCalled">true, if stop was called on the client closing the connection gracefully;
/// false, if the connection has been lost for longer than the
/// <see cref="P:Microsoft.AspNet.SignalR.Configuration.IConfigurationManager.DisconnectTimeout" />.
/// Timeouts can be caused by clients reconnecting to another SignalR server in scaleout.</param>
/// <returns>
/// A <see cref="T:System.Threading.Tasks.Task" />
/// </returns>
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled) {
try {
Guid userName = RetrieveUserId();
string connectionId = Context.ConnectionId;
HubUser user;
Users.TryGetValue(userName, out user);
if (user != null) {
lock (user.ConnectionIds) {
user.ConnectionIds.RemoveWhere(cid => cid.Equals(connectionId));
if (!user.ConnectionIds.Any()) {
HubUser removedUser;
Users.TryRemove(userName, out removedUser);
}
}
}
} catch {
//Bug in SignalR causing Context.User.Identity.Name to sometime be null
//when user disconnects, thus remove the connection manually.
lock (Users) {
HubUser entry = Users.Values.FirstOrDefault(v => v.ConnectionIds.Contains(Context.ConnectionId));
if (entry != null) entry.ConnectionIds.Remove(Context.ConnectionId);
}
}
return base.OnDisconnected(stopCalled);
}
private Guid RetrieveUserId() {
Cookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket decryptedCookie = FormsAuthentication.Decrypt(authCookie.Value);
var user = JsonConvert.DeserializeObject<User>(decryptedCookie.UserData);
return user.Id;
}
}
Then the Hub code is
/// <summary>
/// A hub for sending alerts to users.
/// </summary>
public class AlertHub : HubBase, IAlertHub {
/// <summary>
/// Sends the alert.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="userId">The user identifier.</param>
public void SendAlert(string message, Guid userId) {
HubUser user;
Users.TryGetValue(userId, out user);
if (user != null) {
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<AlertHub>();
context.Clients.Clients(user.ConnectionIds.ToList()).sendAlert(message);
}
}
/// <summary>
/// Send alert to user.
/// </summary>
/// <param name="returnId">The return identifier.</param>
/// <param name="userId">The user identifier.</param>
public void ReturnProcessedAlert(Guid returnId, Guid userId) {
HubUser user;
Users.TryGetValue(userId, out user);
if (user != null) {
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<AlertHub>();
context.Clients.Clients(user.ConnectionIds.ToList()).returnProcessedAlert(returnId);
}
}
}

Create a SOAP header?

How do you add create a SOAP web service header?
Example
<soap:Header>
<myHeader xmlns="https://www.domain.com">
<Username>string</Username>
<Password>string</Password>
</myHeader>
</soap:Header>
source code
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
namespace TestWebServices
{
/// <summary>
/// Summary description
/// </summary>
[WebService(Namespace = "https://Test.com")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Testing : System.Web.Services.WebService
{
[WebMethod]
public string GetTestValue()
{
return "xyz";
}
}
}
How about:
public class MyHeader : SoapHeader
{
public string Username;
public string Password;
}
There's more on the subject here:
Using SOAP Headers (MSDN - archived)
Defining and Processing SOAP Headers (MSDN)
If you need fine grain control over how the soap header xml gets rendered (happens when interfacing with a webservice written with java), you can always override all rendering by implementing IXmlSerializable
[XmlRoot("customHeader", Namespace = "http://somecompany.com/webservices/security/2012/topSecret")]
public class customHeader: SoapHeader, IXmlSerializable
{
public customHeader()
{
Actor = "http://schemas.xmlsoap.org/soap/actor/next";
MustUnderstand = false;
}
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
//throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
//throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("soap:actor", Actor);
writer.WriteAttributeString("soap:mustUnderstand", MustUnderstand ? "1" : "0");
writer.WriteRaw("some encrypted data");
//get it exactly the way you want it in here without mucking with Xml* property attributes for hours on end
//writer.WriteElement(...);
}
}
A SOAP message with headers may look something like the following:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<Username>string</Username>
<Password>string</Password>
</soap:Header>
</soap:Envelope>
EndpointAddressBuilder builder = new EndpointAddressBuilder(client.Endpoint.Address);
AddressHeader header = AddressHeader.CreateAddressHeader("apiKey", "http://tempuri.org", "longapikeyhere");
builder.Headers.Add(header);
client.Endpoint.Address = builder.ToEndpointAddress();

Categories

Resources