I have a Service Contract with 5 operations contracts and hosted in IIS.
Let suppose I have 2 Service Consumer A & B.
I need to give access to all 5 operations contracts to Service Consumer A, but
for Service Consumer B, I need to give access to only 2 out of 5 operations contracts.
What settings we need to do to expose only 2 operations contracts to Consumer B?
Thanks
From Tim's Comment I would create the two contracts and have two endpoints but you would only need one implementation like follows
[ServiceContract]
public interface IService_AdminLvl : IService_USERLvl
{
[OperationContract]
string setData(int value);
[OperationContract]
string setData2(int value);
}
[ServiceContract]
public interface IService_USERLvl
{
[OperationContract]
string GetData(int value);
[OperationContract]
string GetData2(int value);
[OperationContract]
string GetData3(int value);
}
public class Service1 : IService_AdminLvl
{
//IMPL ALL FUCNTIONS
public string GetData(int value)
{
throw new NotImplementedException();
}
public string GetData2(int value)
{
throw new NotImplementedException();
}
public string GetData3(int value)
{
throw new NotImplementedException();
}
public string setData(int value)
{
throw new NotImplementedException();
}
public string setData2(int value)
{
throw new NotImplementedException();
}
and a config something like this...
<service name="domainName.Service_2">
<endpoint address="admin" binding="basicHttpBinding" contract="domainName.IService_AdminLvl">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="user" binding="basicHttpBinding" contract="domainName.IService_UserLvl">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</service>
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 created a WCF service and client in same machine, the services address is wrote into Client's code, so I can easily find the service and create connection to service.
Then I try to deploy them into Intranet. The first problem is: how could Client find the address of server. In actual environment, customers can install service at any computer in Intranet, is there any way to let client find the server address?
WCF service could expose a specific endpoint as a discovery endpoint to all clients so that client could find where the service lies. You could even use UDP multicast to enable the service to be discovered by the client.
You could check the official document.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/wcf-discovery
I have made a demo, wish it is useful to you.
Server.
class Program
{
static void Main(string[] args)
{
using (ServiceHost sh=new ServiceHost(typeof(MyService)))
{
sh.Open();
Console.WriteLine("serivce is ready...");
Console.ReadLine();
sh.Close();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello();
}
public class MyService : IService
{
public string SayHello()
{
return "Hello, I am a Clown";
}
}
Server app.config
<system.serviceModel>
<services>
<service name="DiscoveryEndpoint20181024.MyService" behaviorConfiguration="mybehavior">
<endpoint address="http://10.157.18.188:4800" binding="wsHttpBinding" contract="DiscoveryEndpoint20181024.IService"></endpoint>
<endpoint kind="discoveryEndpoint" address="http://localhost:9999" binding="wsHttpBinding"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="mybehavior">
<serviceMetadata />
<serviceDiscovery />
</behavior>
</serviceBehaviors>
</behaviors>
Client.
class Program
{
static void Main(string[] args)
{
DiscoveryClient client = new DiscoveryClient("my_client");
client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "abcd1234!";
FindCriteria crit = new FindCriteria(typeof(IService));
FindResponse resp = client.Find(crit);
if (resp != null && resp.Endpoints.Count > 0)
{
EndpointDiscoveryMetadata epaddrMtd = resp.Endpoints[0];
ChannelFactory<IService> factory = new ChannelFactory<IService>(new WSHttpBinding(), epaddrMtd.Address);
factory.Credentials.Windows.ClientCredential.UserName = "administrator";
factory.Credentials.Windows.ClientCredential.Password = "abcd1234!";
IService service = factory.CreateChannel();
var result=service.SayHello();
Console.WriteLine(result);
Console.ReadLine();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello();
}
class DemoService : IService
{
public string SayHello()
{
OperationContext context = OperationContext.Current;
return $"the address:{OperationContext.Current.Channel.LocalAddress.Uri}";
}
}
Client.config
<system.serviceModel>
<client>
<endpoint name="my_client" kind="discoveryEndpoint" address="http://10.157.18.188:9999" binding="wsHttpBinding"></endpoint>
</client>
</system.serviceModel>
I am trying to have two contracts under same SVC file, and it is not working. When I open the SVC file in the IE, I get error saying 'Endpoint error'.
If I call the methods of the contract from a client, it wont hit the service.
Servers are hosted on IIS.
Please let me know what is going on here.
Also is it possible to have one wsHttpBinding and one webHttpBinding under same SVC file or web service? They will be hosted on IIS.
SVCFile
<%# ServiceHost Language="C#" Debug="true" Service="S1.ServiceDual6" CodeBehind="ServiceDual6.cs" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
Contracts
{
[InspectorBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ServiceDual6 : IServiceREST, IServiceREST2
{
public string RestInDual_Contract(Message mm)
{
return "";
}
public string RestInDual_Contract2(Message mm)
{
return "";
}
}
}
namespace S1
{
[ServiceContract]
public interface IServiceREST
{
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)]
string RestInDual_Contract(Message mm);
}
[ServiceContract]
public interface IServiceREST2
{
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)]
string RestInDual_Contract2(Message mm);
}
}
Web.config
<service name="S1.ServiceDual6" behaviorConfiguration="s1bhrWithHttpEnabled">
<host>
<baseAddresses>
<add baseAddress="http://localhost/S2/Service6.svc"/>
</baseAddresses>
</host>
<endpoint address="" binding="webHttpBinding" contract="S1.IServiceREST" behaviorConfiguration="s1bhr">
</endpoint>
<endpoint address="" binding="webHttpBinding" contract="S1.IServiceREST2" behaviorConfiguration="s1bhr">
</endpoint>
</service>
You need to differentiate the two services by providing different path if hosted on single endpoint. Separate these two by adding unique value in address field.
<endpoint address="Rest/v1" binding="webHttpBinding" contract="S1.IServiceREST" behaviorConfiguration="s1bhr">
</endpoint>
<endpoint address="Rest/v2" binding="webHttpBinding" contract="S1.IServiceREST2" behaviorConfiguration="s1bhr">
</endpoint>
Now you can invoke them by appending Rest/V1for "S1.IServiceREST" and Rest/V2 for "S1.IServiceREST2" in base address.
I have two endpoints defined pointing to two different service bus topics. With same transportClientEndpointBehavior and on same service.
<endpointBehaviors>
<behavior name="securityBehavior">
<transportClientEndpointBehavior>
<tokenProvider>
<windowsAuthentication>
<stsUris>
<stsUri value="https://on-permises:9355/Namespace" />
</stsUris>
</windowsAuthentication>
</tokenProvider>
</transportClientEndpointBehavior>
</endpointBehaviors>
<customBinding>
<binding name="messagingBinding" >
<textMessageEncoding messageVersion="None" writeEncoding="utf-8" >
<readerQuotas maxStringContentLength="2147483647"/>
</textMessageEncoding>
<netMessagingTransport/>
</binding>
</customBinding>
<endpoint name="endpoint1"
address="sb://on-permises/Namespace/topic1"
listenUri="sb://on-permises/Namespace/topic1/subscriptions/sub"
binding="customBinding"
bindingConfiguration="messagingBinding"
contract="WCFService.IService1"
behaviorConfiguration="securityBehavior" />
<endpoint name="endpoint2"
address="sb://on-permises/Namespace/topic2"
listenUri="sb://on-permises/Namespace/topic2/subscriptions/sub"
binding="customBinding"
bindingConfiguration="messagingBinding"
contract="WCFService.IService2"
behaviorConfiguration="securityBehavior" />
Once i run the application, I get the error :
System.ArgumentException: The value could not be added to the collection, as the collection already contains an item of the same type: 'Microsoft.ServiceBus.TransportClientEndpointBehavior'. This collection only supports one instance of each type.
Parameter name: item
I tried by defining two different endpoint behaviors, but get the same error.
any help here will be helpful.
Found the work around solution, the issue was service host was trying to add these two service endpoints in WSDL/Metadata. which was not necessary. So with the help of ServiceMetadataContractBehavior (IContractBehavior ) stopped exposing the WSDL/Metadata .
Any better approach or correction please let me know.
public class DisableContractMetadata : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
/// Here ServiceMetadataContractBehavior type is derived from IContractBehavior
/// MetadataGenerationDisabled property of ServiceMetadataContractBehavior type = flase disables disables exposing WSDL
contractDescription.ContractBehaviors.Add(new ServiceMetadataContractBehavior() { MetadataGenerationDisabled = true });
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
}
Note that the above answer is incomplete as it doesn't actually implement IContractBehavior.
As this is caused by hosting multiple services, you can also do it in the host:
ContractDescription contractDescription = ContractDescription.GetContract(typeof(MyService));
contractDescription.ContractBehaviors.Add(new ServiceMetadataContractBehavior() { MetadataGenerationDisabled = true });
Or, if you have multiple contracts on a type, use this:
private static void DisableMetadataGeneration(Type serviceType)
{
// prevent this error due to hosting multiple services:
// System.ArgumentException: The value could not be added to the collection, as the collection already contains an item of the same type: 'Microsoft.ServiceBus.TransportClientEndpointBehavior'. This collection only supports one instance of each type.
foreach (Type contractType in GetContracts(serviceType))
{
ContractDescription contractDescription = ContractDescription.GetContract(contractType, serviceType);
contractDescription.ContractBehaviors.Add(new ServiceMetadataContractBehavior() { MetadataGenerationDisabled = true });
}
}
private static Type[] GetContracts(Type serviceType)
{
System.Diagnostics.Debug.Assert(serviceType.IsClass);
Type[] interfaces = serviceType.GetInterfaces();
List<Type> contracts = new List<Type>();
foreach (Type type in interfaces)
{
if (type.GetCustomAttributes(typeof(ServiceContractAttribute), false).Any())
{
contracts.Add(type);
}
}
return contracts.ToArray();
}
I am getting this error:
No Model instance has been assigned to the ProtoOperationBehavior
I just used the WCF template application in Visual Studio to see if I could get it running. How do I fix this error?
Code
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
[ProtoBehavior()]
string GetData(int value);
[OperationContract]
[ProtoBehavior()]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
[DataContract]
[ProtoBuf.ProtoContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember(Order = 1)]
[ProtoBuf.ProtoMember(1)]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember(Order = 2)]
[ProtoBuf.ProtoMember(2)]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
app.config:
<services>
<service name="ProtoBufService.Service1">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:9086/ProtoBufService/Service1/" />
</baseAddresses>
</host>
<endpoint address="basic"
binding="netTcpBinding" contract="ProtoBufService.IService1"
behaviorConfiguration="protoEndpointBehavior">
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
</service>
</services>
<extensions>
<behaviorExtensions>
<add name="protobuf"
type="ProtoBuf.ServiceModel.ProtoBehaviorExtension,
protobuf-net, Version=2.0.0.640,
Culture=neutral, PublicKeyToken=257b51d87d2e4d67" />
</behaviorExtensions>
</extensions>
<endpointBehaviors>
<behavior name="protoEndpointBehavior">
<protobuf />
</behavior>
</endpointBehaviors>
I tried using nuget. That did not work.
I would love to hear more about what problems you had there
I then tried to use the google code version (Core, net30 assembly), same error.
The "core only" library does not include the meta-programming layer - it is designed for use with pre-generated serialization assemblies. As such, there is no default model - one must always be provided.
The simplest "fix" here is simply to use the "full" build of the library; this has the meta-programming model as a default model.