Specifica di un indirizzo endpoint
Tutte le comunicazioni con un servizio Windows Communication Foundation (WCF) vengono eseguite tramite gli endpoint. Ogni ServiceEndpoint contiene una proprietà Address, una proprietà Binding e una proprietà Contract. Il contratto specifica quali operazioni sono disponibili. L'associazione specifica come comunicare con il servizio e l'indirizzo specifica dove trovare il servizio. Ogni endpoint deve avere un indirizzo univoco. L'indirizzo dell'endpoint è rappresentato dalla classe EndpointAddress, che contiene un URI (Uniform Resource Identifier), che rappresenta l'indirizzo del servizio, una proprietà Identity, che rappresenta l'identità di sicurezza del servizio, e una raccolta di proprietà Headers facoltative. Le intestazioni facoltative forniscono informazioni di indirizzamento più dettagliate che consentono di identificare o interagire con l'endpoint. Ad esempio, le intestazioni possono indicare come elaborare un messaggio in ingresso, dove l'endpoint deve inviare un messaggio di risposta o quale istanza di un servizio usare per elaborare un messaggio in ingresso di un particolare utente, quando sono disponibili più istanze.
Definizione di un indirizzo endpoint
In WCF, una classe EndpointAddress modella un riferimento endpoint (EPR), come definito nello standard WS-Addressing.
L'indirizzo URI per la maggior parte dei trasporti è costituito da quattro parti. Questo URI, ad esempio, http://www.fabrikam.com:322/mathservice.svc/secureEndpoint
include le quattro parti seguenti:
Schema: http:
Macchina:
www.fabrikam.com
(Facoltativo) Porta: 322
Percorso: /mathservice.svc/secureEndpoint
Il modello di riferimento endpoint prevede che ogni riferimento possa includere alcuni parametri di riferimento che aggiungono ulteriori informazioni di identificazione. In WCF, questi parametri per riferimento vengono modellati come istanze della classe AddressHeader.
L'indirizzo endpoint per un servizio può essere specificato in modo imperativo mediante l'utilizzo di codice oppure in modo dichiarativo mediante la configurazione. In genere definire endpoint nel codice non è pratico in quanto le associazioni e gli indirizzi di un servizio distribuito sono solitamente diversi da quelli usati durante lo sviluppo del servizio. In genere è più pratico definire endpoint di servizio mediante la configurazione piuttosto che mediante codice. Se le informazioni sull'associazione e sull'indirizzo non vengono incluse nel codice, tali dati possono essere modificati senza dover compilare e distribuire nuovamente l'applicazione. Se non è specificato alcun endpoint nel codice o nella configurazione, il runtime ne aggiunge uno predefinito ad ogni indirizzo di base per ciascun contratto di servizio implementato dal servizio.
Esistono due modo per specificare gli indirizzi degli endpoint per un servizio in WCF. È possibile specificare un indirizzo assoluto per ogni endpoint associato al servizio o fornire un indirizzo di base per la classe ServiceHost di un servizio e quindi specificare un indirizzo per ogni endpoint associato a questo servizio, che viene definito come relativo all'indirizzo di base. È possibile utilizzare entrambe le procedure per specificare gli indirizzi degli endpoint per un servizio nella configurazione o nel codice. Se non si specifica un indirizzo relativo, il servizio utilizza l'indirizzo di base. È inoltre possibile avere più indirizzi di base per un servizio, ma per ogni servizio è consentito un solo indirizzo di base per ogni trasporto. Se sono presenti più endpoint, ognuno dei quali è configurato con un'associazione diversa, i relativi indirizzi devono essere univoci. Gli endpoint che utilizzano la stessa associazione ma contratti diversi possono utilizzare lo stesso indirizzo.
Quando si ospita il servizio in IIS, non si gestisce l'istanza di ServiceHost. Nel caso di un servizio ospitato in IIS, l'indirizzo di base è sempre l'indirizzo specificato nel file con estensione svc. Per gli endpoint del servizio ospitato in IIS è quindi necessario utilizzare sempre indirizzi relativi. Fornire un indirizzo endpoint completo può provocare errori nella fase di distribuzione del servizio. Per altre informazioni, vedere Distribuzione di un servizio WCF ospitato da Internet Information Services.
Definizione degli indirizzi endpoint nella configurazione
Per definire un endpoint in un file di configurazione, usare l'elemento <endpoint>.
<configuration>
<system.serviceModel>
<services>
<service name="UE.Samples.HelloService"
behaviorConfiguration="HelloServiceBehavior">
<endpoint address="/Address1"
binding="basicHttpBinding"
contract="UE.Samples.IHello"/>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="HelloServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Quando viene chiamato il metodo Open, quando cioè l'applicazione host tenta di avviare il servizio, il sistema ricerca un elemento <servizio> con un attributo name che specifica "UE.Samples.HelloService". Se l'elemento <servizio> viene trovato, il sistema carica la classe specificata e crea gli endpoint usando le definizioni degli endpoint presenti nel file di configurazione. Questo meccanismo consente di caricare e avviare un servizio con due righe di codice, mentre tiene le informazioni sull'associazione e l'indirizzamento fuori dal codice. Il vantaggio di tale approccio è che queste modifiche possono essere apportate senza dover ricompilare o ridistribuire l'applicazione.
Le intestazioni facoltative vengono dichiarate in <intestazioni>. Di seguito è riportato un esempio degli elementi usati per specificare gli endpoint di un servizio in un file di configurazione in cui vengono distinte due intestazioni: client "Gold" all'indirizzo http://tempuri1.org/
e client "Standard" all'indirizzo http://tempuri2.org/
. Il client che chiama questo servizio deve disporre delle <intestazioni> appropriate nel file di configurazione.
<configuration>
<system.serviceModel>
<services>
<service name="UE.Samples.HelloService"
behaviorConfiguration="HelloServiceBehavior">
<endpoint address="/Address1"
binding="basicHttpBinding"
contract="UE.Samples.IHello">
<headers>
<Member xmlns="http://tempuri1.org/">Gold</Member>
</headers>
</endpoint>
<endpoint address="/Address2"
binding="basicHttpBinding"
contract="UE.Samples.IHello">
<headers>
<Member xmlns="http://tempuri2.org/">Silver</Member>
</headers>
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="HelloServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Le intestazioni possono essere impostate anche su singoli messaggi anziché su tutti i messaggi su un endpoint (come illustrato in precedenza). A tale scopo, utilizzare OperationContextScope per creare un nuovo contesto in un'applicazione client, per aggiungere un'intestazione personalizzata al messaggio in uscita, come illustrato nell'esempio seguente.
SampleServiceClient wcfClient = new SampleServiceClient(new InstanceContext(this));
try
{
using (OperationContextScope scope = new OperationContextScope(wcfClient.InnerChannel))
{
MessageHeader header
= MessageHeader.CreateHeader(
"Service-Bound-CustomHeader",
"http://Microsoft.WCF.Documentation",
"Custom Happy Value."
);
OperationContext.Current.OutgoingMessageHeaders.Add(header);
// Making calls.
Console.WriteLine("Enter the greeting to send: ");
string greeting = Console.ReadLine();
//Console.ReadLine();
header = MessageHeader.CreateHeader(
"Service-Bound-OneWayHeader",
"http://Microsoft.WCF.Documentation",
"Different Happy Value."
);
OperationContext.Current.OutgoingMessageHeaders.Add(header);
// One-way
wcfClient.Push(greeting);
this.wait.WaitOne();
// Done with service.
wcfClient.Close();
Console.WriteLine("Done!");
Console.ReadLine();
}
}
catch (TimeoutException timeProblem)
{
Console.WriteLine("The service operation timed out. " + timeProblem.Message);
Console.ReadLine();
wcfClient.Abort();
}
catch (CommunicationException commProblem)
{
Console.WriteLine("There was a communication problem. " + commProblem.Message);
Console.ReadLine();
wcfClient.Abort();
}
Dim wcfClient As New SampleServiceClient(New InstanceContext(Me))
Try
Using scope As New OperationContextScope(wcfClient.InnerChannel)
Dim header As MessageHeader = MessageHeader.CreateHeader("Service-Bound-CustomHeader", _
"http://Microsoft.WCF.Documentation", "Custom Happy Value.")
OperationContext.Current.OutgoingMessageHeaders.Add(header)
' Making calls.
Console.WriteLine("Enter the greeting to send: ")
Dim greeting As String = Console.ReadLine()
'Console.ReadLine();
header = MessageHeader.CreateHeader("Service-Bound-OneWayHeader", _
"http://Microsoft.WCF.Documentation", "Different Happy Value.")
OperationContext.Current.OutgoingMessageHeaders.Add(header)
' One-way
wcfClient.Push(greeting)
Me.wait.WaitOne()
' Done with service.
wcfClient.Close()
Console.WriteLine("Done!")
Console.ReadLine()
End Using
Catch timeProblem As TimeoutException
Console.WriteLine("The service operation timed out. " & timeProblem.Message)
Console.ReadLine()
wcfClient.Abort()
Catch commProblem As CommunicationException
Console.WriteLine("There was a communication problem. " & commProblem.Message)
Console.ReadLine()
wcfClient.Abort()
End Try
Indirizzo endpoint nei metadati
Un indirizzo endpoint viene rappresentato in WSDL (Web Services Description Language) come elemento EndpointReference
(EPR) WS-Addressing all'interno dell'elemento wsdl:port
dell'endpoint corrispondente. L'EPR contiene l'indirizzo dell'endpoint e qualsiasi proprietà dell'indirizzo. Si noti che l'EPR in wsdl:port
sostituisce soap:Address
, come illustrato nell'esempio seguente.
Definizione degli indirizzi endpoint nel codice
È possibile creare un indirizzo endpoint nel codice con la classe EndpointAddress. L'URI specificato per l'indirizzo endpoint può essere un percorso completo o un percorso relativo all'indirizzo di base del servizio. Nel codice seguente viene illustrato come creare un'istanza della classe EndpointAddress e aggiungerla all'istanza di ServiceHost che ospita il servizio.
Nell'esempio seguente viene illustrato come specificare l'indirizzo endpoint completo nel codice.
Uri baseAddress = new Uri("http://localhost:8000/HelloService");
string address = "http://localhost:8000/HelloService/MyService";
using (ServiceHost serviceHost = new ServiceHost(typeof(HelloService), baseAddress))
{
serviceHost.AddServiceEndpoint(typeof(IHello), new BasicHttpBinding(), address);
serviceHost.Open();
Console.WriteLine("Press <enter> to terminate service");
Console.ReadLine();
serviceHost.Close();
}
Nell'esempio seguente viene illustrato come aggiungere un indirizzo relativo ("MyService") all'indirizzo di base dell'host del servizio.
Uri baseAddress = new Uri("http://localhost:8000/HelloService");
using (ServiceHost serviceHost = new ServiceHost(typeof(HelloService), baseAddress))
{
serviceHost.AddServiceEndpoint(typeof(IHello), new BasicHttpBinding(), "MyService");
serviceHost.Open();
Console.WriteLine("Press <enter> to terminate service");
Console.ReadLine();
serviceHost.Close();
}
Nota
Le proprietà di ServiceDescription nell'applicazione del servizio non devono essere modificate successivamente al metodo OnOpening su ServiceHostBase. Se vengono modificati dopo questo punto, alcuni membri, ad esempio la proprietà Credentials e i metodi AddServiceEndpoint
su ServiceHostBase e ServiceHost, generano un'eccezione. Altri consentono la modifica, ma il risultato è indefinito.
Analogamente, sul client i valori ServiceEndpoint non devono essere modificati dopo la chiamata a OnOpening su ChannelFactory. Se viene modificata dopo questo punto, la proprietà Credentials genera un'eccezione. Gli altri valori della descrizione client possono essere modificati senza errore, ma il risultato è indefinito.
Per il servizio o per il client, è consigliabile modificare la descrizione prima di chiamare Open.
Uso di endpoint predefiniti
Se non è specificato alcun endpoint nel codice o nella configurazione, il runtime ne fornisce di predefiniti aggiungendone uno ad ogni indirizzo di base per ciascun contratto di servizio implementato dal servizio. L'indirizzo di base può essere specificato nel codice o nella configurazione e gli endpoint predefiniti vengono aggiunti quando viene chiamato Open in ServiceHost.
Se vengono forniti endpoint in modo esplicito, è comunque possibile aggiungere gli endpoint predefiniti chiamando AddDefaultEndpoints su ServiceHost prima di chiamare Open. Per altre informazioni su endpoint, associazioni e comportamenti predefiniti, vedere Simplified Configuration (Configurazione semplificata) e Simplified Configuration for WCF Services (Configurazione semplificata per servizi WCF).