I am using a CustomUserNamePasswordValidator for my WCF web service. However, i am trying to add a IsAlive operation, which should be able to be called from clients, even when not authenticated.
For example, i want to be able to do a check, if a service is online and accessible on startup, so i can notify the user on missing inet connection or a not available service (due to maintenance).
I have code for all this already in place. What i am missing is how i can access the operation without passing a username and password.
I could probably just add a second service which allows anon access, but i'd really prefer to use the existing service.
The Validator is implemented like this (i ommited the actual checking code):
public sealed class MyCredentialValidator : UserNamePasswordValidator
{
public MyCredentialValidator ()
{
}
public override void Validate(string userName, string password)
{
Debug.WriteLine("MyCredentialValidator : Validate called.");
// do some checks
var isValid = CheckCredentials(userName, password)
if(!isValid)
{
throw new FaultException(...);
}
}
}
It is registered in the web.config like so:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="SecureBehavior">
<serviceMetadata httpsGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyCredentialValidator,..."/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<bindings>
<wsHttpBinding>
<binding name="SecureBinding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="2147483647">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
<readerQuotas maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxStringContentLength="2147483647"/>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="my service" behaviorConfiguration="SecureBehavior">
<endpoint address="" binding="wsHttpBinding" contract="my contract" bindingConfiguration="SecureBinding">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
client side configuration:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="SecureBinding"
closeTimeout="00:10:00"
openTimeout="00:10:00"
receiveTimeout="00:10:00"
sendTimeout="00:10:00"
maxReceivedMessageSize="2147483647">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
<readerQuotas maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxStringContentLength="2147483647"/>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://my service url"
contract="my contract"
binding="wsHttpBinding"
bindingConfiguration="SecureBinding"
name="secure" />
</client>
</system.serviceModel>
client side wcf call code:
var cf = new ChannelFactory<my contract>("secure");
using (IClientChannel channel = (IClientChannel)cf.CreateChannel())
{
channel.OperationTimeout = TimeSpan.FromSeconds(3);
bool success = false;
try
{
channel.Open();
result = ((my contract)channel).IsAlive();
channel.Close();
success = true;
}
finally
{
if (!success)
{
channel.Abort();
}
}
}
I have done something like this before,
depending on how you have integrated your custom validator in the wcf pipleline,
you could simply before you do the actual validation, which I guess returns something like true or false, you could check the incoming url or address and see if it is going to be going to your IsAlive operation, if that is the case, you could simply do a early return true.
Wcf has a few ways with which you can check what operation the client has called.
to be more accurate, I would need to know how you wrote your custom validator and where in the pipeline it integrates.
Related
I'm trying to create a callback in WCF service. Service so far was using basicHttpBinding, so I want to add another end point for netTcpBinding. Service is already hosted in IIS. First It was hosted in IIS 6, but then I installed IIS 7.
So, I'm getting the following error:
The requested service, 'net.tcp://localhost:1801/MyServiceName.svc/NetTcpExampleAddress' could not be activated. See the server's diagnostic trace logs for more information.
When seeing the log, this is the message:
So the main error is:
Contract requires Duplex, but Binding 'BasicHttpBinding' doesn't support it or isn't configured properly to support it.
Here are my config files:
My Web.config for the server:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="demoServiceNetTcpBinding">
<security mode="None"/>
</binding>
</netTcpBinding>
<basicHttpBinding>
<binding name="demoServiceHttpBinding" receiveTimeout="00:05:00" sendTimeout="00:05:00" maxReceivedMessageSize="2147483647">
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="MyServerName.MyServiceName">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:1801/MyServiceName.svc/"/>
<add baseAddress="http://localhost:1800/MyServiceName.svc/"/>
</baseAddresses>
</host>
<endpoint
address="NetTcpExampleAddress"
binding="netTcpBinding"
bindingConfiguration="demoServiceNetTcpBinding"
contract="MyServerName.SharedContract.IMyServiceName"/>
<endpoint
address="BasicHttpExampleAddress"
binding="basicHttpBinding"
bindingConfiguration="demoServiceHttpBinding"
contract="MyServerName.SharedContract.IMyServiceName"/>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
My App.config for the client:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="demoServiceNetTcpBinding">
<security mode="None"/>
</binding>
</netTcpBinding>
<basicHttpBinding>
<binding name="demoServiceHttpBinding" receiveTimeout="00:05:00" sendTimeout="00:05:00" maxReceivedMessageSize="2147483647">
<security mode="None"/>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint name="NetTcpExampleName"
address="net.tcp://localhost:1801/DicomQueryService.svc/NetTcpExampleAddress"
bindingConfiguration ="demoServiceNetTcpBinding"
contract="MyServerName.SharedContract.IMyServiceName"
binding="netTcpBinding" />
<endpoint name="BasicHttpExampleName"
address="http://localhost:1800/MyServiceName.svc/BasicHttpExampleAddress"
bindingConfiguration ="demoServiceHttpBinding"
contract="MyServerName.SharedContract.IMyServiceName"
binding="basicHttpBinding" />
</client>
</system.serviceModel>
Settings in my IIS:
If there are any other pieces of code that you need, please let me know and I'll update the question.
EDIT 1:
Here are more details from the code, of how I'm calling the service from the client (on client side):
public class MyCommandClass : IMyServiceCallback
{
public MyCommandClass()
{
var ctx = new InstanceContext(new MyCommandClass());
DuplexChannelFactory<MyServerName.SharedContract.IMyServiceName> channel = new DuplexChannelFactory<MyServerName.SharedContract.IMyServiceName>(ctx, "NetTcpExampleName");
MyServerName.SharedContract.IMyServiceName clientProxy = channel.CreateChannel();
clientProxy.MyFunction(); //debug point is comming here and then it throws the error
clientProxy.ProcessReport();
(clientProxy as IClientChannel).Close();
channel.Close();
}
public void Progress(int percentageCompleted)
{
Console.WriteLine(percentageCompleted.ToString() + " % completed");
}
}
where interfaces (on server side) are defined as:
[ServiceContract(CallbackContract = typeof(IMyServiceCallback))]
public interface IMyServiceName
{
[OperationContract]
void MyFunction();
[OperationContract(IsOneWay = true)]
void ProcessReport();
}
public interface IMyServiceCallback
{
[OperationContract(IsOneWay = true)]
void Progress(int percentageCompleted);
}
and service (on server side) is defined as:
public class MyServiceName: IMyServiceName
{
public void MyFunction()
{
//do something
}
public void ProcessReport()
{
//trigger the callback method
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
OperationContext.Current.GetCallbackChannel<IMyServiceCallback>().Progress(i);
}
}
}
My methods so far are just a demo. Once the error related to this question is fixed, then I'll start with developing the methods.
Your service contract requires duplex connection (you have ServiceCallback attribute). Therefore all endpoints that this service exposes must support duplex connection. Net.tcp does support it, but basicHttp does not, so you cannot use basicHttp with your service now.
Default.aspx.cs
WCFService.Service1Client client = new WCFService.Service1Client();
string stream = client.JsonSerializeFromDatabase();
client.Close();
WCFService.Service1Client client2 = new WCFService.Service1Client();
foreach (WCFService.Person in client2.JsonDeserializeFromDatabase(stream))
Service1.svc.cs
public IList<Person> JsonDeserializeFromDatabase(string value)
{
MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(value));
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(List<Person>));
IList<Person> tableData = (IList<Person>)ser.ReadObject(ms);
ms.Close();
ms.Dispose();
return tableData;
}
IService1.cs
[OperationContract]
IList<Person> JsonDeserializeFromDatabase(string value);
Server Web.config
<httpRuntime maxRequestLength="8192"/>
</system.web>
...
<system.serviceModel>
<services>
<service name="TestWCF.Service1" behaviorConfiguration="TestWCF.Service1Behavior">
<endpoint address="" binding="wsHttpBinding" contract="TestWCF.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="TestWCF.Service1Behavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
</serviceBehaviors>
Client Web.config
<httpRuntime maxRequestLength="8192"/>
</system.web>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="debuggingBehaviour">
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1" closeTimeout="00:50:00" openTimeout="00:50:00" receiveTimeout="00:50:00" sendTimeout="00:50:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="64" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
<reliableSession ordered="true" inactivityTimeout="00:50:00" enabled="false"/>
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="~~~~~/Service1.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1" contract="WCFService.IService1" name="WSHttpBinding_IService1" behaviorConfiguration="debuggingBehaviour">
Exception Information
- Type: System.ServiceModel.CommunicationException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- Message: An error occurred while receiving the HTTP response to ~~~~~/Service1.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.
I got this exception information from Server trace viewer, so please do not advise me to put <-system.diagnostics-> tag.
As you can see, I increased all the size thing.
Like.. i don't know why I am getting an error when I call JsonDeserializeFromDatabase(stream).
"An error occurred while receiving the HTTP response to ~~~~~/Service1.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
I too have experienced this error message when returning records from a database in a WCF service. As well as increasing maxReceivedMessageSize in the binding in the client configuration (App.config), a separate problem seems to be that WCF has problems serializing Entity Framework objects if they have relationships that lead to circularity in their object graphs.
I solved this by returning buddy class objects (which are copies of the raw database records, but without any relationship links) rather than the raw database classes themselves.
Hope this helps -
And WHY doesn't Microsoft produce better error messages?? Here, as in many other cases, the error message gives no clue to the real problem (the serialization of the return value from the WCF call)!
re: WCF & problems serializing Entity Framework objects if they have relationships that lead to circularity in their object graphs. I was getting the same error and the answer provided by user1956642 and it did point me in the right direction, but later realized I could serialize these entities by configuring the DbContext
context.Configuration.ProxyCreationEnabled = false;
Lazy loading is still enabled, but I believe the dynamic proxies are used for change tracking and lazy loading. So yea ... just my 5c
I'm trying to implement a service that will allow users to watch and upload videos to a WCF service from a windows phone 7 client (something like youtube). Now, I have the basic implementation of the service which sends video files (.wmv) to a client which has a MediaElement implementation of the Silverlight framework (has some differences from .NET implementation of the same class). Now, whenever I try to play the video locally on the client I get a SecurityException was unhandled error. When I try to encapsulate the service call in a try/catch block the application just hangs there.
Here's the code:
Server-side:
class TransferService: ITransferService
{
public FileStream DownloadFile(string filename)
{
//string FilePath = Path.Combine(#"c:\Uploads", filename);
FileStream result = File.Open(#"C:\Uploads\test.wmv", FileMode.Open, FileAccess.Read, FileShare.Read);
return result;
}
public void UploadFile(FileStream request)
{
//Not yet implemented
}
}
Server-side(web.config):
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime maxRequestLength="100240" />
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttp" allowCookies="true"
maxReceivedMessageSize="20000000"
maxBufferSize="20000000"
maxBufferPoolSize="20000000" transferMode="Buffered">
<readerQuotas maxDepth="200000000"
maxArrayLength="200000000"
maxStringContentLength="200000000"
maxBytesPerRead="200000000"
maxNameTableCharCount="200000000"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="VideoService.TransferService" behaviorConfiguration="VideoServiceTypeBehaviors" >
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
<endpoint contract="VideoService.ITransferService" binding="basicHttpBinding" address="basic" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="VideoServiceTypeBehaviors" >
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Client-side:
public partial class Page1 : PhoneApplicationPage
{
TransferServiceClient sc;
public Page1()
{
InitializeComponent();
sc = new TransferServiceClient();
this.Loaded += new RoutedEventHandler(Page_Loaded);
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
sc.DownloadFileCompleted += new EventHandler<DownloadFileCompletedEventArgs>(sc_DownloadFileCompleted); //I think the problem is here
sc.DownloadFileAsync("test.wmv");
}
void sc_DownloadFileCompleted(object sender, DownloadFileCompletedEventArgs e)
{
myMediaElement.SetSource(e.Result);
myMediaElement.Play();
}
Client-side(ServiceReference.clientconfig):
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ITransferService" closeTimeout="00:02:00"
openTimeout="00:02:00" receiveTimeout="00:10:00" sendTimeout="00:02:00"
maxBufferSize="210005536" maxReceivedMessageSize="210005536"
textEncoding="utf-8">
<security mode="None" />
</binding>
<binding name="BasicHttpBinding_ITransferService1" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:53163/TransferService.svc/basic"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ITransferService1"
contract="TransferService.ITransferService" name="BasicHttpBinding_ITransferService" />
</client>
</system.serviceModel>
</configuration>
Any help or insight would be much appreciated. Thanks
first i would try to use this code block correctly:
sc.DownloadFileCompleted += new EventHandler(sc_DownloadFileCompleted); //I think the problem is here
sc.DownloadFileAsync("test.wmv");
myMediaElement.Play();
you fire up an asynchronous download-process and you are attaching a "download complete"-callback to it...
why do you call "myMediaElement.Play" synchronously AFTER the asynchronous invoke?
at this time, the file isn't downloaded yet and maybe the mediaelement fires the exception, because the file is locked (because of the download).
you have to call "myMediaElement" in the "sc_DownloadFileCompleted"-Handler AFTER the asynchronous download process finished....
please check if this was the prob...
I'm having some difficulty setting up a WCF service to run under Windows authentication. The service is only consumed via jQuery using ajax.
IIS (version 6 on server 2003) is set to only allow Windows Authentication.
web.config has the <authentication mode="Windows" /> tag.
Here's the service section of the web.config:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="AspNetAjaxBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<services>
<service name="SearchService" behaviorConfiguration="ServiceBehavior">
<endpoint address="http://localhost:9534/SearchService.svc" behaviorConfiguration="AspNetAjaxBehavior"
binding="webHttpBinding" bindingConfiguration="webWinBinding"
name="searchServiceEndpoint" contract="MyApp.Services.ISearchService">
</endpoint>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="webWinBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
<readerQuotas maxArrayLength="100000" maxStringContentLength="2147483647" />
</binding>
</webHttpBinding>
</bindings>
The interface looks like this:
[ServiceContract(Namespace = "http://MyService.ServiceContracts/2012/02", Name = "SearchService")]
public interface ISearchService
{
[WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "GetSomeData?filter={filter}")]
[OperationContractAttribute(Action = "GetSomeData")]
string GetSomeData(string filter);
}
And the implementation:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SearchService : ISearchService
{
public string GetSomeData(string filter)
{
// Call Database and get some results
// return the results
return "";
}
}
When I navigate to the service in Internet Explorer, it prompts me for my username and password, despite having Windows Authentication turned on.
As soon as I enable Anonymous Authentication, the service loads just fine and everything works. Problem is, I have other things going on in the web application that require anonymous to be turned off.
I've scoured the web and can't find anything on this problem.
I have two WCF RESTful services - the "general" service is public and has no security; the "admin" service I intend to use basic authentication over SSL. This is my server side web.config:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="general" maxReceivedMessageSize="2147483647">
<readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647" />
<security mode="None">
<transport clientCredentialType="None" />
</security>
</binding>
<binding name="admin" maxReceivedMessageSize="2147483647">
<readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647" />
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="MyNamespace.AppServices.GeneralService">
<endpoint address="" binding="webHttpBinding" contract="MyNamespace.Contracts.IGeneralService" behaviorConfiguration="web" bindingConfiguration="general" />
</service>
<service name="MyNamespace.AppServices.AdminService">
<endpoint address="" binding="webHttpBinding" contract="MyNamespace.Contracts.IAdminService" behaviorConfiguration="web" bindingConfiguration="admin" />
</service>
</services>
</system.serviceModel>
On the client side, I currently have code that looks like this:
private static IGeneralService GetGeneralChannel()
{
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.None;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.MaxReceivedMessageSize = Int32.MaxValue;
binding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
binding.ReaderQuotas.MaxArrayLength = Int32.MaxValue;
WebChannelFactory<IGeneralService> cf = new WebChannelFactory<IGeneralService>(binding, new Uri("http://localhost:1066/GeneralService"));
IGeneralService channel = cf.CreateChannel();
return channel;
}
private static IAdminService GetAdminChannel()
{
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.MaxReceivedMessageSize = Int32.MaxValue;
binding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
binding.ReaderQuotas.MaxArrayLength = Int32.MaxValue;
WebChannelFactory<IAdminService> cf = new WebChannelFactory<IAdminService>(binding, new Uri("http://localhost:1066/AdminService"));
cf.Credentials.UserName.UserName = "myUserName";
cf.Credentials.UserName.Password = "myPassword";
IAdminService channel = cf.CreateChannel();
return channel;
}
The question is, since I obviously do not want to hard-code all of this configuration information, how do I need to provide it in the web.config on the client? It is pretty clear to me that the binding element needs to look pretty much the same on the client as it does on the server. However, where do I indicate the credentials that are assigned to the WebChannelFactory?
Any help and/or insight will be appreciated.
Thanks,
Steve
You cannot put those credentials (username and password) into web.config and have WCF read them from there. This is one of the very few features in WCF which cannot be done in config - you have to set those credentials in your code.
Of course, in your code, you can read them from e.g. a database table, or a config entry somewhere - but you have to do that yourself. WCF can't be configured to automagically read those settings from somewhere.