how to use [PrincipalPermission(SecurityAction.Demand, Role = "Administrators")] attribute on a class?
I am looking for some way to restrict the access on my object
i.e
if some object is being accessed in a service method and if the user has rights for accessing the service method but does not have rights accessing the object an exception should be thrown
PrincipalPermission attribute can adorn method or class. Therefore it is possible to restrict access to an instance of an object. Several things need to be done:
Configure selected service and client binding to use security. Specify Windows as client credential type.
Configure service to use Windows groups for authorization.
Adorn class that will contain confidential information with PrincipalPermission attribute.
If singleton instance needs to be passed to ServiceHost constructor, do following:
Create service singleton instance. Thread.CurrentPrincipal must have permissions necessary to access the confidential object.
Create ServiceHost instance by passing service singleton instance. Property InstanceContextMode of ServiceBehavior attribute must be set to InstanceContextMode.Single.
Otherwise:
Create ServiceHost instance by passing the service type.
Optionally, adorn the service method with FaultContract attribute and throw FaultException from it in order to avoid faulting the client channel.
Here is an example:
Service configuration file:
<system.serviceModel>
<services>
<service name="Server.Service" behaviorConfiguration="Authorization">
<endpoint address=""
binding="netTcpBinding" bindingConfiguration="TCP"
contract="Common.IService" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:13031/Service"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="TCP" openTimeout="00:30:00" closeTimeout="00:00:10" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
<security mode="Message">
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="Authorization">
<serviceAuthorization principalPermissionMode="UseWindowsGroups" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Client configuration file:
<system.serviceModel>
<client>
<endpoint name="NetTcpBinding_IService"
address="net.tcp://localhost:13031/Service"
binding="netTcpBinding" bindingConfiguration="TCP"
contract="Common.IService" />
</client>
<bindings>
<netTcpBinding>
<binding name="TCP" openTimeout="00:30:00" closeTimeout="00:00:10" sendTimeout="00:30:00" receiveTimeout="00:30:00" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
<security mode="Message">
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
Confidential information class:
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators" ) ]
public class ContactInfo
{
public string FirstName { get; set; }
public string LastName { get; set; }
public ContactInfo()
{
FirstName = "John";
LastName = "Doe";
}
public override string ToString()
{
return string.Format( "{0} {1}", FirstName, LastName );
}
}
Service contract and its implementation:
[ServiceContract]
public interface IService
{
[OperationContract]
[FaultContract( typeof( string ) )]
string GetName( int id );
}
[ServiceBehavior]
// Use following if singleton instance needs to be passed to `ServiceHost` constructor
//[ServiceBehavior( InstanceContextMode = InstanceContextMode.Single )]
public class Service : IService
{
private Dictionary<int, ContactInfo> Contacts { get; set; }
public Service()
{
Contacts = new Dictionary<int, ContactInfo>();
IPrincipal originalPrincipal = Thread.CurrentPrincipal;
try
{
Thread.CurrentPrincipal = new WindowsPrincipal( WindowsIdentity.GetCurrent() );
Contacts.Add( 1, new ContactInfo() );
}
finally
{
Thread.CurrentPrincipal = originalPrincipal;
}
}
public string GetName( int id )
{
if ( Contacts.Count < id )
return null;
try
{
return Contacts[ id ].ToString();
}
catch ( Exception ex )
{
throw new FaultException<string>( ex.Message );
}
}
}
If you are familiar with .NET permission coding (either imperative or declarative), the pattern is exactly the same. In the declarative form, the PrincipalPermissionAttribute is applied to the method in the class that implements the service’s contract:
[PrincipalPermission(SecurityAction.Demand, Role = "Updaters")]
public bool Update()
{
return true;
}
In this example, the current principal is checked to see whether it belongs to a role called Updaters. In the actual implementation of the attribute, the IsInRole method on the principal is called.
For imperative determination of the PrincipalPermissionAttribute, an instance of the PrincipalPermission class is created. The constructor for PrincipalPermission takes the username and role as a parameter. When instantiated, the Demand method can be called to determine whether the current principal has the necessary permissions. The following code provides an example:
PrincipalPermission p = new PrincipalPermission(null, "Updaters");
p.Demand();
the configuration should look like this:
<behaviors>
<serviceBehaviors>
<behavior>
...
<serviceAuthorization principalPermissionMode="UseWindowsGroups" />
</behavior>
</serviceBehaviors>
</behaviors>
for a working sample please look at: Authorizing Access to Service Operations
Related
I used this MSDN example to construct my console host app:
http://msdn.microsoft.com/en-us/library/ms731758.aspx
It works I have a service running, while running I can add it as a Service Reference to my Silverlight class library ViewModel and I can see it running in a browser.
However when I run my Silverlight app I get the following error message:
Could not find default endpoint element that references contract
'ServiceLayer.IServiceLayer' in the ServiceModel client configuration
section. This might be because no configuration file was found for
your application, or because no endpoint element matching this
contract could be found in the client element.
This is my service code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation;
using Model;
namespace ServiceLayer
{
[ServiceContract(Namespace = "ServiceLayer")] //This has to match references or updating the service reference will not work.
public interface IServiceLayer
{
[OperationContract] //This is needed for each method.
string Compile(string cscode, string name, string type, int token);
[OperationContract] //This is needed for each method.
string LoginClick(string username, string password, bool createuser, int token);
[OperationContract]
bool IsLoggedIn(int token);
[OperationContract] //This is needed for each method.
object[] GetMessages(int token);
[OperationContract] //This is needed for each method.
int GetToken(int token);
[OperationContract] //This is needed for each method.
string[][] GetPlugins(int token);
[OperationContract] //This is needed for each method.
string Finalize(int token, string[] descriptions);
[OperationContract] //This is needed for each method.
object[][] Communicate(string methodName, int token, object[] args);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ServiceLayer : IServiceLayer
{
public string Compile(string cscode, string name, string type, int token)
{
// Add your operation implementation here
ModelInterface MI = new ModelInterface();
return MI.Compile(cscode, name, type, token);
}
public String LoginClick(string username, string password, bool createuser, int token)
{
ModelInterface MI = new ModelInterface();
return MI.LoginClick(username, password, createuser, token);
}
public bool IsLoggedIn(int token)
{
ModelInterface MI = new ModelInterface();
return MI.IsLoggedIn(token);
}
public object[] GetMessages(int token)
{
ModelInterface MI = new ModelInterface();
return MI.GetMessages(token);
}
public int GetToken(int token)
{
ModelInterface MI = new ModelInterface();
return MI.GetToken(token);
}
public string[][] GetPlugins(int token)
{
ModelInterface MI = new ModelInterface();
return MI.GetPlugins(token);
}
public string Finalize(int token, string[] descriptions)
{
ModelInterface MI = new ModelInterface();
return MI.Finalize(token, descriptions);
}
public object[][] Communicate(string methodName, int token, object[] args)
{
ModelInterface MI = new ModelInterface();
return MI.Communicate(methodName, token, args);
}
// Add more operations here and mark them with [OperationContract]
}
}
This is the ServiceReferences.ClientConfig of the ViewModel:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IServiceLayer" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
<binding name="BasicHttpBinding_IServiceLayer1" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:3263/front" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IServiceLayer" contract="IServiceLayer"
name="BasicHttpBinding_IServiceLayer" />
<endpoint address="http://localhost:3263/front" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IServiceLayer1" contract="ServiceLayer.IServiceLayer"
name="BasicHttpBinding_IServiceLayer1" />
</client>
</system.serviceModel>
And this is the Web.Config file of the project with the website hosting the Silverlight app.
I post it because I tried this Could not find default endpoint element
guide - ie copying the servicemodel section from the VM config to the web.Config below also:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<pages controlRenderingCompatibilityVersion="4.0"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="OrgOS.Web.ServerCommunicationService.customBinding0">
<binaryMessageEncoding/>
<httpTransport/>
</binding>
<binding name="OrgOS.Web.ServiceLayer.customBinding0">
<binaryMessageEncoding/>
<httpTransport/>
</binding>
</customBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
<services>
<service name="OrgOS.Web.ServiceLayer">
<endpoint address="" binding="customBinding" bindingConfiguration="OrgOS.Web.ServiceLayer.customBinding0" contract="OrgOS.Web.ServiceLayer"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
My server I rented came without ASP.NET despite the fact I paid for it, support is on leave and I have deadlines coming up!
Trying to get WCF working is driving me mad - please help me...
Why is it so freaking hard to show a simple Silverlight project...
Ah nevermind; you have to post the config settings in both the View Silverlight project, the web project AND the ViewModel Silverlight project.
You must also delete all endpoints and bindings, but one to avoid getting things confused.
Also changed things to make sure namespace="namespace" fits the actual namespace.
... not that things work for that reason, oh noes all I get is invalid results.
FML right now.
I have that service
[OperationContract]
[WebGet(UriTemplate = "/GetData")]
List<FieldInfo> GetSerializedData();
and web.config
<system.web>
<webServices>
<protocols>
<add name="HttpGet" />
<add name="HttpPost" />
</protocols>
</webServices>
<httpRuntime executionTimeout="90" maxRequestLength="1048576" useFullyQualifiedRedirectUrl="false" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="100"/>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpBindingSettings" maxBufferPoolSize="524288" maxReceivedMessageSize="654321" sendTimeout="00:10:00" closeTimeout="00:01:00" openTimeout="00:10:00" receiveTimeout="00:10:00">
<security mode="None">
<transport clientCredentialType="None" />
</security>
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
</binding>
</webHttpBinding>
<wsHttpBinding>
<binding name="wsHttpBindingSettings" maxBufferPoolSize="524288" maxReceivedMessageSize="654321" sendTimeout="00:10:00" closeTimeout="00:01:00" openTimeout="00:10:00" receiveTimeout="00:10:00">
<security mode="None">
<transport clientCredentialType="None" />
</security>
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="MetadataBehavior" name="ServiceModel.Service">
<endpoint name="soap" address="soap" behaviorConfiguration="Default" binding="wsHttpBinding"
bindingConfiguration="wsHttpBindingSettings" contract="ServiceModel.IService" />
<endpoint name="Json" address="json" behaviorConfiguration="JSON" binding="webHttpBinding"
bindingConfiguration="webHttpBindingSettings" contract="ServiceModel.IService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://service.com/Service.svc/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MetadataBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="JSON">
<webHttp automaticFormatSelectionEnabled="true"/>
<dataContractSerializer maxItemsInObjectGraph="10000000"/>
</behavior>
<behavior name="Default">
<dataContractSerializer maxItemsInObjectGraph="10000000"/>
</behavior>
</endpointBehaviors>
</behaviors>
Why on the client side only one endpoint is generated ?
<client>
<endpoint address="http://service.renault.com/Service.svc/soap"
binding="wsHttpBinding" bindingConfiguration="soap" contract="ServiceReference1.IService"
name="soap" />
</client>
My point is to execute service method from asp.net page codebehind and wcf return data in soap or json depends on ContentType. But how to set in asp.net page content type to application/json when it have text/html content. I have problem with understand it.
Why on the client side only one endpoint is generated?
Because WCF does not emit metadata for non-SOAP endpoints. Unlike WSDL and MEX for SOAP, there's no widely used metadata format for "REST" endpoints (WADL is one of them, but it's not much used and not implemented by WCF), so on the Add Service Reference (or svcutil) will only see one endpoint in the metadata and only that one will be created.
I want to use WCF feature which select proper serialization type depends on ContentType of request
JSON vs XML is a serialization type decision; JSON vs SOAP is not (SOAP is a well-defined protocol, with rules for what the request should look like) - see more information on WCF Dynamic Response Format. Your webHttBinding-endpoint will do that (return JSON or XML based on the incoming request), since you enabled auto format selection, but the way you'd consume this service doesn't need to be with a WCF client - using WebClient, HttpWebRequest should work out just fine.
If possible, try to design you Visual Studio like this :
Solution
project with contracts (only the IXXXXService)
web project with the implementation and all the endpoints (reference the contract project)
clients project not using the VS generated proxy, but a factory that can select the correct endpoint and so protocol. (reference the contract project)
Here is a sample class I use in a scenario similar to yours :
public class ServiceHelper
{
/// <summary>
/// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service
/// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
/// </summary>
/// <typeparam name="TService">The type of the service to use</typeparam>
/// <param name="action">Lambda of the action to performwith the service</param>
[System.Diagnostics.DebuggerStepThrough]
public static void UsingProxy<TService>(Action<TService> action)
where TService : ICommunicationObject, IDisposable, new()
{
var service = new TService();
bool success = false;
try
{
action(service);
if (service.State != CommunicationState.Faulted)
{
service.Close();
success = true;
}
}
finally
{
if (!success)
{
service.Abort();
}
}
}
/// <summary>
/// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service
/// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
/// </summary>
/// <typeparam name="TIServiceContract">The type of the service contract to use</typeparam>
/// <param name="action">Action to perform with the client instance.</param>
/// <remarks>In the configuration, an endpoint with names that maches the <typeparamref name="TIServiceContract"/> name
/// must exists. Otherwise, use <see cref="UsingContract<TIServiceContract>(string endpointName, Action<TIServiceContract> action)"/>. </remarks>
[System.Diagnostics.DebuggerStepThrough]
public static void UsingContract<TIServiceContract>(Action<TIServiceContract> action)
{
UsingContract<TIServiceContract>(
typeof(TIServiceContract).Name,
action
);
}
/// <summary>
/// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service
/// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
/// </summary>
/// <typeparam name="TIServiceContract">The type of the service contract to use</typeparam>
/// <param name="action">Action to perform with the client instance.</param>
/// <param name="endpointName">Name of the endpoint to use</param>
[System.Diagnostics.DebuggerStepThrough]
public static void UsingContract<TIServiceContract>(
string endpointName,
Action<TIServiceContract> action)
{
var cf = new ChannelFactory<TIServiceContract>(endpointName);
var channel = cf.CreateChannel();
var clientChannel = (IClientChannel)channel;
bool success = false;
try
{
action(channel);
if (clientChannel.State != CommunicationState.Faulted)
{
clientChannel.Close();
success = true;
}
}
finally
{
if (!success) clientChannel.Abort();
}
}
}
In the client config, I set up manually my references :
<system.serviceModel>
<client>
<endpoint address="http://localhost/myapp/myservice.svc/soap"
binding="wsHttpBinding"
contract="MyProject.Contracts.IMyService"
name="IMyServiceSoap"/>
<endpoint address="http://localhost/myapp/myservice.svc/rest"
binding="webHttpBinding"
contract="MyProject.Contracts.IMyService"
name="IMyServiceRest"/>
</client>
</system.serviceModel>
Then, in your code you can simply call :
ServiceHelper.UsingContract<"IMyServiceSoap", MyProject.Contracts.IMyService>(
svc => svc.DoTheJob()
);
or
ServiceHelper.UsingContract<"IMyServiceRest", MyProject.Contracts.IMyService>(
svc => svc.DoTheJob()
);
[edit] The server config is similar to this one :
<services>
<service name="MyService">
<endpoint address="soap"
binding="wsHttpBinding"
contract="MyContracts.IMyService"/>
<endpoint address="rest"
binding="webHttpBinding"
contract="MyContracts.IMyService"/>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"/>
</service>
</services>
I have a requirement of creating a webservice where the client and the service will talk in Simple Soap (that request and response will be soap), I tried all to find a sample example on net where this thing is already done or some code sample so that I can get started but I think I am bad in searching google, that is why can't find any one so far, Some one suggested to use WCF so get an article
http://csharping.com/wcf/building-a-soap-response-envelope-manually-with-the-message-class/
But again my problem is not solved, I tried to create an application with this sample (with so many issues :( )
Created a console application and the Program.cs is
using System;
using System.IO;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using System.Runtime.Serialization;
namespace ServiceConsole
{
public class Program
{
static void Main(string[] args)
{
using (ServiceHost serviceHost = new ServiceHost(typeof(ServiceClient), new Uri("http://localhost:2000/")))
{
ServiceEndpoint serviceEndpoint = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IService)));
ServiceEndpoint metadataEndpoint = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IMetadataExchange)));
ServiceMetadataBehavior metadataBehavior = serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
serviceHost.Description.Behaviors.Add(metadataBehavior);
}
serviceHost.AddServiceEndpoint(typeof(IService), new BasicHttpBinding(), "http://localhost:2000/");
serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "http://localhost:2000/WCFService/mex");
serviceHost.Open();
string requestData = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Header><h:HeaderItem xmlns:h=\"http://tempuri.org/\">a header item</h:HeaderItem><ActivityId CorrelationId=\"090c553b-bfcc-4e4f-94cd-1b4333fe82a9\" xmlns=\"http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics\">377a454b-b543-4c6f-b4ac-3981029b60e6</ActivityId></s:Header><s:Body><string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/\">a body item</string></s:Body></s:Envelope>";
byte[] requestDataBytes = Encoding.UTF8.GetBytes(requestData);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost/WCFService/");
request.Method = "POST";
request.ContentType = "text/xml; charset=utf-8";
request.Headers.Add("SOAPAction", "http://tempuri.org/IWebService/GetMessage");
request.ContentLength = requestDataBytes.Length;
StreamWriter streamWriter = new StreamWriter(request.GetRequestStream());
streamWriter.Write(requestData);
streamWriter.Flush();
streamWriter.Close();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader streamReader = new StreamReader(response.GetResponseStream());
string responseBody = streamReader.ReadToEnd();
Console.WriteLine("Service returned the following response...");
Console.WriteLine("");
Console.WriteLine(responseBody);
Console.ReadKey();
serviceHost.Close();
}
}
}
}
the app.config which I generated using svcutil.exe is like this
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:2000/WebService/Service.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService"
contract="IService" name="BasicHttpBinding_IService" />
</client>
</system.serviceModel>
</configuration>
My webservioce is like (it is a WCF website in which the port is provided by me and is 2000
Service contract is
[ServiceContract]
public interface IService
{
[OperationContract]
Message GetMessage(Message s);
}
[ServiceBehavior]
public class Service : IService
{
public Message GetMessage(Message message)
{
string body = message.GetBody<string>();
return Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IWebService/GetMessageResponse", "body is " + body);
}
}
and the web.config is
<system.serviceModel>
<services>
<service behaviorConfiguration="ServiceBehavior" name="Service">
<endpoint address="http://localhost:2000/WebService/Service.svc" binding="basicHttpBinding" bindingConfiguration=""
contract="IService" >
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<timeouts closeTimeout="00:01:10" />
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" policyVersion="Policy15" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
My issue is 405 webmethod not body can anyone please tell me what is an issue in this, I am new to WCF before this created a sample and this is my second application
You do not need to create the SOAP message manually, just use basic http binding and that will rturn SOAP.
When you have a WCF service, the whole point is that you can define e.g. parameters like strings, int and so forth - and you don't have to mess with loads of XML and SOAP headers and bodies.
So basically, your service contract should be something like:
[ServiceContract]
public interface IService
{
[OperationContract]
int DoSomeMathAddTowNumbers(int num1, int num2);
}
and your service implementation would then just implement that method, add the two numbers, and return the result:
public class Service : IService
{
int DoSomeMathAddTowNumbers(int num1, int num2)
{
return num1 + num2;
}
}
No mess with Message or XML manipulation or anything.
A client that wants to call your service would create a WCF client-side proxy using svcutil or the Visual Studio Add Service Reference method, and it would get a proxy class that has the same methods as the service it connects to - and you would call them, using the straight, easy parameters - something like:
ServiceClient client = new ServiceClient();
int result = client.DoSomeMathAddTwoNumbers(42, 100);
So basically, I think you need to get back to the drawing board and read up on the WCF basics again - it should not be that difficult, really! (that's the whole point - it should make services easy ...)
Check out the Beginner's Guide at the WCF Developer Center at MSDN - it contains lots of really good videos, screencasts, articles on how to get started with WCF.-
You might also want to check out the DotNet Rocks TV episode #135: Keith Elder Demystifies WCF
My first question so be gentle =)
The following is all .Net 4, VB.Net, in VS2010. The end goal is to receive a (complete) stream from the client over Tcp binding, to an IIS Hosted WCF Service. The problem I am facing is that the service is not able to read any bytes from the provided stream. Now with the nitty gritty... I've removed a fair amount for brevity but, let me know if I've omitted something important.
The service contract is as follows:
<ServiceContract(Namespace:="ImageSystem")> _
Public Interface IUploadService
<OperationContract()> _
Function UploadFile(ByVal file As ImageUpload) As ImageUpload
End Interface
The data contract ImageUpload is as follows:
<MessageContract()> _
Public Class ImageUpload
#Region " Message Header "
Private _ImageID As Nullable(Of Long)
<MessageHeader()> _
Public Property ImageID() As Nullable(Of Long)
Get
Return _ImageID
End Get
Set(ByVal value As Nullable(Of Long))
_ImageID = value
End Set
End Property
'... a few other value type properties
#End Region
#Region " Message Body"
' Do not add any more members to the message body or streaming support will be disabled!
<MessageBodyMember()> _
Public Data As System.IO.Stream
#End Region
End Class
The relevant server config/bindings are as follows (these are obviously dev environment settings only):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpStreamBinding" transferMode="Streamed" maxBufferSize="20971520" maxReceivedMessageSize="20971520"/>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="UploadServiceBehaviour"
name="ImageSystem.SVC.UploadService">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpStreamBinding"
contract="ImageSystem.SVC.IUploadService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:809/UploadService" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="UploadServiceBehaviour">
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
The WCF Service is a service Library, which is hosted by a Web Application. The Web Application project runs in my local IIS 7.5. IIS has been configured to enable TCP connections, and the application pool identity is configured with relevant permissions to the contract implementation. VS2010 is run as admin to enable debugging in IIS.
To test the contract implementation I have a Windows Console Application set up as a (test) client. The client proxy classes were generated by adding a service reference to the service within the IIS host (http://localhost/ImageSystem/UploadService.svc). The service reference is configured to generate async methods.
The relevant auto-generated client config is as follows (note, I've tried increasing maxBufferPoolSize, maxBufferSize, and maxReceivedMessageSize to match the servers config of "20971520", but to no avail):
[EDIT: reliableSessions section commented out in light of Sixto Saez's suggestion but to no avail]
<system.serviceModel>
<bindings>
<binding name="NetTcpBinding_IUploadService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Streamed" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="20971520"
maxBufferSize="20971520" maxConnections="10" maxReceivedMessageSize="20971520">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<!--<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />-->
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://mycomputername.mydomain/ImageSystem/UploadService.svc"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IUploadService"
contract="UploadService.Local.IUploadService" name="NetTcpBinding_IUploadService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
The client usage is as follows:
Public Sub Test()
Dim serviceClient As UploadService.Local.UploadServiceClient = New UploadService.Local.UploadServiceClient
AddHandler serviceClient.UploadFileCompleted, AddressOf LocalTestCallback
Dim ms As MemoryStream = New MemoryStream
My.Resources.Penguins.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "MYDOMAIN"
serviceClient.ClientCredentials.Windows.ClientCredential.UserName = "User"
serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "Password123"
serviceClient.UploadFileAsync(Nothing, ..., ms, ms) '"..." is obviously not actually here, other values omitted. "ms" is passed as UserState object in addition to fulfilling the 'Data' parameter
End Sub
In case you wonder (or it matters), the penguins image is the one provided with Windows 7 in the sample pictures directory. The image is 777,835 bytes (should be within the relevant request/buffer max sizes).
I have tried two approaches to read the image on the server side.
Approach 1:
Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile
Dim uploadBuffer(Helper.Settings.AppSettings(Of Integer)("UploadBufferSize", True) - 1) As Byte
Dim ms As MemoryStream = New MemoryStream()
Dim bytesRead As Integer
Do
bytesRead = file.Data.Read(uploadBuffer, 0, uploadBuffer.Length)
ms.Write(uploadBuffer, 0, bytesRead)
Loop Until bytesRead = 0
End Function
Approach 2:
Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile
Dim reader As StreamReader = New StreamReader(file.Data)
Dim imageB64 As String = reader.ReadToEnd
ms = New MemoryStream(Convert.FromBase64String(imageB64))
End Function
In both cases, ms.Length = 0. More clearly, in the second approach, imageB64 = "" (empty string).
Why aren't I receiving anything from the stream? Also, as a sneaky sub-question, why does the generated proxy class not provide an overload that accepts an object of type ImageUpload?
Thank you in advance!!
it seemed strange to me that you would have the problems you mentioned, so I was curious and put together an implementation using your service contract. That one actually worked straight away. I don't actually know what went wrong in your case (it's not obvious), but let me just post a working solution here hoping that this will help you to solve your problem.
Unfortunately, as I abandoned VB many years ago, I can only provide C# code. Hope that's alright.
Server Web.config (tested in IIS, with net.tcp binding):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding transferMode="Streamed" maxReceivedMessageSize="1000000">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="ImageSystem.SVC.UploadService">
<endpoint address="" binding="netTcpBinding" contract="ImageSystem.SVC.IUploadService">
</endpoint>
<endpoint address="mex" kind="mexEndpoint" binding="mexTcpBinding"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Client app.config (console test app):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding transferMode="Streamed" maxReceivedMessageSize="1000000">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint
address="net.tcp://localhost/WcfService1/UploadService.svc"
binding="netTcpBinding"
contract="ImageServices.IUploadService"
name="NetTcpBinding_IUploadService">
</endpoint>
</client>
</system.serviceModel>
Service contract & implementation:
[ServiceContract(Namespace="urn:ImageSystem")]
public interface IUploadService
{
[OperationContract]
ImageUpload UploadFile(ImageUpload file);
}
[MessageContract]
public class ImageUpload
{
[MessageHeader]
public long? ImageID { get; set; }
[MessageBodyMember]
public Stream Data;
}
public class UploadService : IUploadService
{
public ImageUpload UploadFile(ImageUpload file)
{
long length;
using (var ms = new MemoryStream())
{
file.Data.CopyTo(ms);
length = ms.Length;
}
return new ImageUpload { ImageID = length, Data = new MemoryStream() };
}
}
Test app:
private static readonly string imgPath = #"C:\Pictures\somepicture.jpg";
private static readonly EventWaitHandle waitHandle = new AutoResetEvent(false);
static void Main()
{
long? result;
using (var service = new ImageServices.UploadServiceClient("NetTcpBinding_IUploadService"))
{
var image = new ImageServices.ImageUpload();
using (var imgStream = File.OpenRead(imgPath))
{
image.Data = imgStream;
service.UploadFileCompleted += (sender, e) =>
{
result = e.Result;
if (e.Data != null) image.Data.Dispose();
waitHandle.Set();
};
service.UploadFileAsync(null, imgStream);
waitHandle.WaitOne();
}
}
}
First of all, as you can see, the config files can be a lot simpler. Especially the large BufferSize value is not necessary. Then, with regard to the service contract, it's not clear to me why the Upload operation would receive AND return an ImageUpload message. In my implementation, I'm returning the uploaded file size in the ImageID parameter just for demo purposes, of course. I don't know what your reasoning behind that contract was, and what you actually would want to return.
Actually, was just about to click "Send" when I had an idea why your code could have failed. In your test client, before you call serviceClient.UploadFileAsync(), add this line to your code: ms.Seek(0, SeekOrigin.Begin).
This resets the position of the MemoryStream back to its beginning. If you don't do that, the MemoryStream will be consumed only from its current position, which is its end - and which explains the Length = 0 of the stream received on the service side!
You may have already seen the information in this MSDN article but you should review the section called Streaming Data and the restrictions it lists. Your client config shows the reliableSession element having the ordered attribute set to "true" which is not supported for streaming.
I don't know if that is specifically the cause of your issue but it is a start. That article also layouts pretty well the basic config required for streaming so you should ensure your config complies with its recommendations.
The method that I am trying to call has the following signature:
Results GetPerformanceData(MyEntity entity, bool recurse);
I set a breakpoint at the beginning of the method, but the exception is thrown before code execution gets to the breakpoint.
Tracing the WCF service gives me the following info:
System.NullReferenceException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Object reference not set to an instance of an object.
StackTrace:
at System.Runtime.Serialization.CodeGenerator.VerifyParameterCount(MethodInfo methodInfo, Int32 expectedCount)
at System.Runtime.Serialization.CodeGenerator.Call(Object thisObj, MethodInfo methodInfo, Object param1)
at ...
How can I determine what the null variable is? I have my debugger set to break on all exceptions, but it isn't breaking for this particular problem. It looks like the "entity" parameter to the method I am trying to call cannot deserialize properly.
Sample invocation call
var results = client. GetPerformanceData(entity, true);
WCF service config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="All"
propagateActivity="true">
<listeners>
<add name="traceListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "trace.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBindingSettings" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="5242880" maxBufferPoolSize="52428800" maxReceivedMessageSize="5242880"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="64" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="Company.Service.Engine"
behaviorConfiguration="ServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/Engine"/>
</baseAddresses>
</host>
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="basicHttpBindingSettings"
contract="Company.Service.IEngine"/>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
It turns out that I had some properties tagged as [DataMember] that had getters but no setters.
Usually, I'd get a descriptive error by looking at the trace file on the service, or in a pop-up window when adding the service reference on the client project. This time around I did not.
I had to search for ALL instances of [DataMember] and looked at them one by one to find the properties that were missing a setter.
I know this question is old but if somebody else stumbles upon this question this might help in solving the problem
I just had this problem and discovered something new about the [DataMember] Annotation, I had 2 classes and one of these classes had a reference to the other(let's call them class A and B), where some of the properties and fields in class B was referred to in class A.
here's a little example of what I had
[DataContract]
public class A
{
[Datamember]
public B classb {get;set;}
[DataMember]
public int Number
{
get {return classb.Number;}
set {classb.Number = value;}
}
}
[DataContract]
public class B
{
[DataMember]
public int Number
{
get;
set;
}
}
the thing is that the null reference was there because datamember means that itself holds a value where class A just holds a reference to the number in class B, what you do now is put a Datamember on only one of the numbers.
I was also getting a NullReferenceException from System.Runtime.Serialization.CodeGenerator.VerifyParameterCount when I changed the concrete type passed to a data contract, to one that was missing the [DataContract] attribute.
[DataContract, KnownType(typeof(NewEngine))]
public class Car
{
[DataMember]
public IEngine Engine { get; set; }
}
[DataContract] // DON'T FORGET THIS
public class NewEngine : IEngine { ... }
I found another cause for that error: assigning the same DataMember.Name to two properties caused the error for me:
[DataContract]
public class Car
{
[DataMember(Name = "Type")]
public string EngineType { get; set; }
[DataMember(Name = "Type")]
public string TyresType { get; set; }
}
I know it is a big mistake but, if the properties are many... it could happen :-)
There could be several issues here. Make sure your datacontracts have the proper attributes. Make sure your service references are up to date, and if not reusing your datacontracts by referencing the assemblies make sure the generation of the objects is correct (the generated code is usually in a file called Reference.cs). also try placing a try catch block around every service operation and wrap those exceptions in FaultExceptions. This could help you find a better answer.
try
{
...
}
catch (Exception ex)
{
// The generic argument might be unnecessary
throw new FaultException<Exception>(ex);
}