The code below suppose to run a self hosted with custom authentication WCF Service which needs to provide its services to a Silverlight 4 client (See code below).
The result is that the infamous clientaccesspolicy Security Error communication exception is thrown even though the clientaccesspolicy.xml is visible in browser and shows no SSL error. The clientaccesspolicy.xml breakpoint is not hit.
I realize I only need to specify the entry but I've tried
various games with the clientaccesspolicy.xml which didnt work.
Your help is appreciated
1) This is the app.config and code for the service:
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<system.serviceModel>
<client />
<bindings>
<basicHttpBinding>
<binding name="slBindingWithUserNamePassValidator">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
<webHttpBinding>
<binding name="capService" crossDomainScriptAccessEnabled="true">
<security mode="Transport" />
</binding>
<binding name="capServiceNoSSL" crossDomainScriptAccessEnabled="true">
<security mode="None" />
</binding>
</webHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="svcBehavior" name="WCF_Self_Hosted_UserName_Validator.Service1">
<endpoint address="" binding="webHttpBinding" bindingConfiguration="capService" behaviorConfiguration="capServiceBehavior"
contract="WCF_Self_Hosted_UserName_Validator.ICAPService" />
<endpoint address="" binding="webHttpBinding" bindingConfiguration="capServiceNoSSL" behaviorConfiguration="capServiceBehavior"
contract="WCF_Self_Hosted_UserName_Validator.ICAPService" />
<endpoint address="MyCustomValidationService" binding="basicHttpBinding" bindingConfiguration="slBindingWithUserNamePassValidator"
contract="WCF_Self_Hosted_UserName_Validator.IService1">
</endpoint>
<host>
<baseAddresses>
<add baseAddress="https://(somesite):9999/" />
<add baseAddress="http://(somesite):9998/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="svcBehavior">
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="capServiceBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>
The code for the service:
Using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.IdentityModel.Selectors;
using System.IO;
using System.ServiceModel.Web;
namespace WCF_Self_Hosted_UserName_Validator
{
class Program
{
static void Main(string[] args)
{
MyServiceHost host = new MyServiceHost(new Service1());
host.Open();
Console.WriteLine("Host open...");
Console.ReadLine();
}
}
public class MyServiceHost : ServiceHost
{
SecurityValidator _securityValidator = null;
public MyServiceHost(IService1 svc) : base(svc)
{
Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
_securityValidator = new SecurityValidator();
Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = _securityValidator;
Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "my-fqdn-valid-cert.dot.something");
}
}
public class SecurityValidator : UserNamePasswordValidator
{
public SecurityValidator()
{
}
public override void Validate(string userName, string password)
{
try
{
if (userName != "1" && password != "1")
throw new FaultException("auth error");
}
catch (Exception ex)
{
throw ex;
}
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetPrivateInfo();
}
[ServiceContract]
public interface ICAPService
{
[OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
Stream GetClientAccessPolicy();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service1 : IService1, ICAPService
{
public string GetPrivateInfo()
{
return "Some info " + DateTime.Now.ToShortTimeString();
}
public System.IO.Stream GetClientAccessPolicy()
{
WebOperationContext ctx = new WebOperationContext(OperationContext.Current);
string txtCap = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers=""*"">
<domain uri=""*""/>
<domain uri=""http://*""/>
<domain uri=""https://*""/>
</allow-from>
<grant-to>
<resource include-subpaths=""true"" path=""/""/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>";
WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
MemoryStream response = new MemoryStream(Encoding.UTF8.GetBytes(txtCap));
return response;
}
}
}
2) We have a CA signed SSL cert in the MY container of the LOCAL MACHINE and used netsh
netsh http add sslcert ipport=0.0.0.0:9999 certhash=aabbcc_thumbprint
appid={my_app_id_guid} clientcertnegotiation=enable
The above executes succesfully and the host loads properly and allows creating a new silverlight project.
3) The silverlight project is a just an new silveright project with add service reference and the following code:
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
ServiceReference1.Service1Client c = new ServiceReference1.Service1Client();
c.ClientCredentials.UserName.UserName = "1";
c.ClientCredentials.UserName.Password = "1";
c.GetPrivateInfoCompleted += new EventHandler<ServiceReference1.GetPrivateInfoCompletedEventArgs>(c_GetPrivateInfoCompleted);
c.GetPrivateInfoAsync();
}
void c_GetPrivateInfoCompleted(object sender, ServiceReference1.GetPrivateInfoCompletedEventArgs e)
{
if (e.Error == null)
{
this.Content = new TextBlock() { Text = e.Result };
}
else
{
this.Content = new TextBlock() { Text = e.Error.GetBaseException().Message };
}
}
}
}
4) This is the ServiceReferences.ClientConfig generated
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="TransportWithMessageCredential" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://(TheAddress)/MyCustomValidationService"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />
</client>
</system.serviceModel>
</configuration>
Blockquote
netsh http add sslcert ipport=0.0.0.0:9999 certhash=aabbcc_thumbprint appid={my_app_id_guid} clientcertnegotiation=enable
You've used the netsh with the clientcertnegotiation flag that means the server requires client certificate. When Silverlight calls the clientaccesspolicy, it does not send a client certificate, and that is why you get the exception.
If you don't need the client certificate remove this flag.
I'm not sure if SL is able to send a client certificate when fetching the clientaccesspolicy, but if your web page also access that site the browser should use the certificate you gave it. So yo ucan try adding a link to the secured site in your hosting html/aspx which will require you to select a certificate and then SL will use that certificate
Related
I'm trying to assemble a .Net 6 WCF Service with WCFCore, using a basicHttpBinding, and I'm strugling to add a service authorization manager.
My purpose is to enable WCF to read and validate bearer tokens and use OAuth. I can't move to REST because of legacy applications compatibility, so I need to keep WCF but use bearer tokens.
My service at this stage is quite simple:
[ServiceContract]
public interface IService
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
public class Service : IService
{
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;
}
}
// 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; }
}
}
My Program.cs:
var builder = WebApplication.CreateBuilder();
builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelConfigurationManagerFile("wcf.config");
builder.Services.AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>();
builder.Services.AddSingleton<OAuthAuthorizationManager>();
var app = builder.Build();
app.UseServiceModel(bld =>
{
bld.AddServiceEndpoint<Service, IService>(new BasicHttpBinding(BasicHttpSecurityMode.Transport), "/Service.svc");
var mb = app.Services.GetRequiredService<ServiceMetadataBehavior>();
mb.HttpsGetEnabled = true;
});
app.Run();
Then my wcf.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicBinding" receiveTimeout="00:10:00">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="CoreWCFService.Service" behaviorConfiguration="Default">
<endpoint address="basic" binding="basicHttpBinding" bindingConfiguration="basicBinding" contract="CoreWCFService.IService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
<serviceAuthorization serviceAuthorizationManagerType="CoreWCFService.OAuthAuthorizationManager,CoreWCFService" />
<dataContractSerializer maxItemsInObjectGraph="10000000" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
But when I call the service with tokens, nothing happens on the authorization manager, the operation runs simply ignoring this service behavior.
Is there anyone out there that can help me with this?
You may refer to the Corewcf project template. There are a few things to note:
The interface and its implementation need to be separated to facilitate subsequent maintenance and invocation of the interface.
We need to look at the UseServiceModel part in Program.cs.
I'm going to implement basic authentication in WCF. I'm very new in all this stuff and my program is based on this series of articles http://leastprivilege.com/2008/01/11/http-basic-authentication-against-non-windows-accounts-in-iisasp-net-part-0-intro/ I do use webHttpBinding and HTTPS is on.
So the main idea is implementation of IHttpModule in this way:
When user requests some resource a module checks if Authorization header is present.
In case of Authorization is present, the module extracts the header's value, decodes and checks login and pass
In the other case the module sends a response with 401 code and a header "WWW-Authenticate".
Here is my implementation of the module:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Management;
using System.Text;
namespace MyProj_A
{
public class MyHTTPModule : IHttpModule
{
void IHttpModule.Dispose()
{
}
void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += Context_BeginRequest;
context.AuthenticateRequest += OnEnter;
context.EndRequest += OnLeave;
}
private void Context_BeginRequest(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
context.Response.Write("BeginRequest");
}
void OnEnter(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
if (IsHeaderPresent())
{
if (!AuthenticateUser())
{
DenyAccess();
}
}
else
{
// if anonymous requests are not allowed - end the request
DenyAccess();
}
}
bool IsHeaderPresent()
{
return HttpContext.Current.Request.Headers["Authorization"] != null;
}
bool AuthenticateUser()
{
string username = "", password = "";
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic"))
{
// extract credentials from header
string[] credentials = ExtractCredentials(authHeader);
username = credentials[0];
password = credentials[1];
if (username.CompareTo("tikskit") == 0 && password.CompareTo("") == 0)
{
return true;
} else
{
return false;
}
}
else
{
return false;
}
}
private static void DenyAccess()
{
HttpContext context = HttpContext.Current;
context.Response.StatusCode = 401;
context.Response.End();
}
void OnLeave(object sender, EventArgs e)
{
// check if module is enabled
if (HttpContext.Current.Response.StatusCode == 401)
{
SendAuthenticationHeader();
}
}
private void SendAuthenticationHeader()
{
HttpContext context = HttpContext.Current;
context.Response.StatusCode = 401;
context.Response.AddHeader(
"WWW-Authenticate",
"Basic realm=\"yo-ho-ho\""
);
}
}
}
I publish it under IIS 7.5 on remote computer and connect to it with remote debugger from my Visual Studio. I set breakpoints at Context_BeginRequest, OnEnter and OnLeave.
Then I access to my WCF from a browser using URL and here is what happens:
After I inputted an URL and pressed the Enter Context_BeginRequest is fired
In VS I can see that the Authorization header isn't present
OnEnter is fired and eventually it assigns 401 code to the response
OnLeave is executed as well and it sets WWW-Authenticate to the response header
In the browser the standart login dialog is shown
I input the user name and password and press OK
Now Context_BeginRequest is fired again and I can see that Authorization header is present and consists a value like "Basic ", which is right
OnEnter isn't executed at all this time
OnLeave is fired but a value of HttpContext.Current.Response.StatusCode is 401 by some reason
Here is my Web.config
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2"/>
<customErrors mode="Off" />
</system.web>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<webHttp automaticFormatSelectionEnabled="false"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Default" >
<serviceMetadata httpGetEnabled="false" />
<serviceMetadata httpsGetEnabled="false"/>
<serviceAuthenticationManager authenticationSchemes="Basic"/>
<serviceCredentials>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="MyProj_A.Service1">
<endpoint address="" binding="webHttpBinding" contract="MyProj_A.IService1"
behaviorConfiguration="webBehavior"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/" />
</baseAddresses>
</host>
</service>
</services>
<diagnostics>
<endToEndTracing activityTracing="false" messageFlowTracing="true" propagateActivity="true"></endToEndTracing>
</diagnostics>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="MyHTTPModule"
type="MyProj_A.MyHTTPModule,MyProj-A"/>
</modules>
<directoryBrowse enabled="false"/>
</system.webServer>
</configuration>
So my questions are
1. Why OnEnter isn't fired second time, in 8, and how is 401 assigned in an item 9?
2. How to work around this behaviour, I mean do I need to move all the authentication processing from AuthenticateRequest (OnLeave) to BeginRequest (Context_BeginRequest) for example? Or maybe there is a better place for such processing?
Thanks!
Case is closed
I've forgotten to refer to binding configuration in endpoint configuration:
<endpoint address="" binding="webHttpBinding"
contract="MyProj_A.IService1"
behaviorConfiguration="webBehavior"
**bindingConfiguration="MyBinding"**/>
var dataToSend = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(mi));
var req = HttpWebRequest.Create("http://localhost/Service1.svc/json/MethodName");
req.ContentType = "application/json";
req.ContentLength = dataToSend.Length;
req.Method = "POST";
req.GetRequestStream().Write(dataToSend, 0, dataToSend.Length);
var response = req.GetResponse();
Here "/json" is my endpoint address and my service is configured with multiple endpoints. As per image here, request i sent is recieving null at server.
If my request format is not proper then suggest proper way to call this service.
// Service inter face
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method="POST")]
Response MethodName(Request request);
}
// Service1
public class Service1 : IService
{
public Response MethodName(Request request)
{
some logical operation....
}
}
// End point configuration (Web config)
<endpoint address="json" behaviorConfiguration="jsonBehavior"
binding="webHttpBinding" bindingConfiguration="webHttpBindingJson"
name="jsonn" contract="Service1.IService" />
<endpoint address="xml" behaviorConfiguration="poxBehavior" binding="webHttpBinding"
bindingConfiguration="webHttpBindingXml" name="xmll" contract="Service1.IService" />
<endpointBehaviors>
<behavior name="jsonBehavior">
<enableWebScript />
</behavior>
<behavior name="poxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<webHttpBinding>
<binding name="webHttpBindingJson">
<security mode="None" />
</binding>
<binding name="webHttpBindingXml">
<security mode="None" />
</binding>
</webHttpBinding>
// Request class
[DataContract]
public class Request
{
string userMobile;
string otp;
[DataMember]
public string UserMobile
{
get { return userMobile; }
set { userMobile = value; }
}
[DataMember]
public string OTP
{
get { return otp; }
set { otp = value; }
}
}
Finally i found for this.
I modified endpoint of json behaviour configuration to this,
<behavior name="jsonBehavior">
<webHttp defaultBodyStyle ="Bare"/>
<!--<enableWebScript />-->
</behavior>
and removed enableWebScript. Finally my code working.
I have created a wcf service in order to test wcf time outs. my problem is even time out has expired its still works.
In this service i have created long running method and in there i create log file,then i let to service time out.but even service time out has expired still log file appending data till to the long running method finish execution.?
How this happen? are there is a way to stop that ?
service timeout is 1 min
long running method duration is :10 mins
This service hosted in IIS 7.5 using WAS
here is the my service implementation class
public class LongRunner : ILongRunner
{
public void LongRunnerMethod()
{
int counter = int.Parse(ConfigurationManager.AppSettings["val"]);
string text = string.Empty;
for (int i = 0; true; i++)
{
Thread.Sleep(1000);
if (i >= counter)
break;
text = string.Concat(i.ToString(), DateTime.Now.ToString());
File.AppendAllText(#"C:\Looger\log.txt", text);
}
}
}
Here is the my service interface
[ServiceContract]
public interface ILongRunner
{
[OperationContract]
void LongRunnerMethod();
}
Finally here is the web config
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="val" value="600"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="ORServiceBehavior" name="PTS.LongRunner">
<endpoint binding="netTcpBinding" bindingConfiguration="DefaultNetTcpBinding" name="ORServiceTCPEndPoint"
contract="PTS.ILongRunner" address="" >
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration="" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8079/___/_________.svc" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="DefaultNetTcpBinding" maxBufferSize="2147483647" maxConnections="10" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" >
<reliableSession enabled="false" ordered="false" inactivityTimeout="00:10:00"/>
<readerQuotas maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxDepth="32" maxNameTableCharCount="2147483647" maxStringContentLength="2147483647"/>
<security mode="Message">
<transport clientCredentialType="Windows"/>
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ORServiceBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceThrottling maxConcurrentCalls="200" maxConcurrentSessions="200" maxConcurrentInstances="200" />
<dataContractSerializer maxItemsInObjectGraph="50000" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
WCF has many timeouts...
binding timeouts: SendTimeout, OpenTimeout and CloseTimeout (default 1 minute), ReceiveTimeout (default 10 minutes)
service host: OpenTimeout (default 1 minute), CloseTimeout (default 10 seconds)
reliable session inactivity timeout: default 10 minutes
anything that inherits ConnectionOrientedTransportBindingElement e.g. NetTcp: ChannelInitializationTimeout default 30 seconds
My guess is that you have gotten "Unhandled Exception: System.TimeoutException: The open operation did not complete within the allotted timeout of ...." - this means that proxy has timed out waiting for service to send a response.
"how this happens? [that service keep logging even if you have a "operation did not complete" timeout]"
ServiceHost has allocated a thread to process the request made to the LongRunnerMethod method. This thread will finish processing the request until something catastrophic (e.g. process host shutdown) or exception is raised from inside a method.
"is there a way to stop that [interrupt the thread processing the method call]?"
You would need to get the signal from external source that you need to check on every n-th iteration to see if you should continue. You would need to pass something from the client in a form of unique id (ticket) to the first method then use this id with a Cancel method that will set the signal for the LongRunnerMethod to abort processing.
Here is an example using msmq which allows true one-way call:
SERVER
class Program
{
static void Main(string[] args)
{
var baseAddress = "net.msmq://localhost/private/";
var address = "ILongRunner";
var host = new ServiceHost(typeof (LongRunner), new Uri(baseAddress));
var binding = new NetMsmqBinding(NetMsmqSecurityMode.None);
var se = host.AddServiceEndpoint(typeof (ILongRunner), binding, address );
se.VerifyQueue(); //comes from IDesign ServiceModelEx http://www.idesign.net/Downloads/GetDownload/1887
host.Open();
Console.WriteLine("Press any key to stop");
Console.ReadLine();
}
}
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
public class LongRunner : ILongRunner
{
private static readonly ConcurrentBag<Guid> Cancelations = new ConcurrentBag<Guid>();
public void CancelLongRunnerMethod(Guid id)
{
if (Cancelations.All(z => z != id))
Cancelations.Add(id);
}
public void LongRunnerMethod(Guid id)
{
int counter = 300000;
//concurrent write will require different technique
var file = string.Format(#"D:\log.{0}.txt", id);
for (int i = 0; true; i++)
{
//check every 5th call
if (i % 5 == 0)
{
if (Cancelations.Any(z => z == id))
{
Guid cancelationId;
Cancelations.TryTake(out cancelationId);
if (cancelationId == id)
{
Debug.WriteLine(string.Format("LongRunnerMethod {0} canceled", id));
return;
}
}
}
Thread.Sleep(10);
Console.WriteLine("at " + i);
if (i >= counter)
break;
var text = string.Format("{0} {1} \n", i.ToString(), DateTime.Now.ToString());
File.AppendAllText(file, text);
}
Console.WriteLine("Complete " + id);
}
}
[ServiceContract()]
public interface ILongRunner
{
[OperationContract(IsOneWay = true)]
void CancelLongRunnerMethod(Guid id);
[OperationContract(IsOneWay = true)]
void LongRunnerMethod(Guid id);
}
CLIENT
class Program
{
static void Main(string[] args)
{
var baseAddress = "net.msmq://localhost/private/";
var address = "ILongRunner";
var binding = new NetMsmqBinding(NetMsmqSecurityMode.None);
var c1 = new ChannelFactory<ILongRunner>(binding, new EndpointAddress(new Uri(baseAddress + address)));
var proxy = c1.CreateChannel();
var request1 = Guid.NewGuid();
proxy.LongRunnerMethod(request1);
var co = c1 as ICommunicationObject;
co.Close();
var c2 = ChannelFactory<ILongRunner>.CreateChannel(binding, new EndpointAddress(new Uri(baseAddress + address)));
var request2 = Guid.NewGuid();
c2.LongRunnerMethod(request2);
Thread.Sleep(5000);
var c3 = new ChannelFactory<ILongRunner>(binding, new EndpointAddress(new Uri(baseAddress + address)));
var proxy2 = c3.CreateChannel();
proxy2.CancelLongRunnerMethod(request1);
var co2 = c3 as ICommunicationObject;
co2.Close();
}
}
I have a problem configuring a ServiceBehavior for my WCF service.
Some background.
Basically I am developing a REST service WCF that is supposed to run on IIS.
I need to be able to log exceptions thrown by the service (I'm using log4net) and return HTTP status codes depending on the type of exception.
I want my service implementation to have a minimum knowledge of WCF related stuff, so I don't want to convert the exceptions to FaultException everywhere in the service.
So I figured out that adding my own IErrorHandler to the service host would be the best way to do it.
My problem is however that no matter what I try I can't seem to get the configuration for my custom ServiceBehavior right in Web.config.
Here is the relevant code.
Web config.
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</modules>
</system.webServer>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="UsingErrorLogBehavior">
<errorLogBehavior/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior>
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="errorLogBehavior"
type="MyNameSpace.Web.ErrorExtensionElement, MyNameSpace.Web"/>
</behaviorExtensions>
</extensions>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true"
automaticFormatSelectionEnabled="false"
defaultOutgoingResponseFormat="Json"
maxReceivedMessageSize="4194304" transferMode="Buffered" />
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
ErrorExtensionElement.
namespace MyNameSpace.Web
{
public class ErrorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(ErrorServiceBehavior); }
}
protected override object CreateBehavior()
{
return new ErrorServiceBehavior();
}
}
}
ErrorServiceBehavior.
namespace MyNameSpace.Web
{
public class ErrorServiceBehavior : IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
channelDispatcher.ErrorHandlers.Add(new ExceptionModule());
}
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
}
}
Where ExceptionModule implements IErrorHandler.
You have a <serviceBehavior> section named "UsingErrorLogBehavior", but no service configurations are referencing that section. You can either make that section the default service behavior (by not giving it a name, like you have for the endpoint behavior), or add a <service> element for your service which references that behavior:
<services>
<service name="YourNamespace.YourServiceName"
behaviorConfiguration="UsingErrorLogBehavior">
<endpoint address=""
binding="webHttpBinding"
contract="YourNamespace.YourContractName" />
</service>
</services>