我目前正在使用WebServiceHost
在Windows服务(而不是IIS)中运行一些WCF REST服务。我为每个服务定义了一个单独的接口和类,但是我有一些问题需要了解WebServiceHost
,ServiceEndpoint
和ServiceContracts
如何一起用来创建自主解决方案。
我当前设置的方式是我为每个实现服务的类创建一个新的WebServiceHost
,并使用该类的名称作为URI的一部分,然后定义URI的其余部分界面。
[ServiceContract]
public interface IEventsService
{
[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebGet(UriTemplate = "EventType", ResponseFormat=WebMessageFormat.Json)]
List<EventType> GetEventTypes();
[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebGet(UriTemplate = "Event")]
System.IO.Stream GetEventsAsStream();
}
public class EventsService: IEventsService
{
public List<EventType> GetEventTypes() { //code in here }
public System.IO.Stream GetEventsAsStream() { // code in here }
}
创建服务的代码如下所示:
Type t = typeof(EventService);
Type interface = typeof(IEventService);
Uri newUri = new Uri(baseUri, "Events");
WebServicesHost host = new WebServiceHost(t, newUri);
Binding binding = New WebHttpBinding();
ServiceEndpoint ep = host.AddServiceEndpoint(interface, binding, newUri);
这很有效,每个服务的服务端点都是在适当的URL上创建的。
http://XXX.YYY.ZZZ:portnum/Events/EventType
http://XXX.YYY.ZZZ:portnum/Events/Event
然后我重复另一个服务接口和服务类。我想删除Url中的Events
,但如果我这样做并使用相同的基本网址创建多个WebServiceHosts
,我会收到错误:
The ChannelDispatcher at 'http://localhost:8085/' with contract(s) '"IOtherService"' is unable to open its IChannelListener
内部异常:
"A registration already exists for URI 'http://localhost:8085/'."
我试图了解WebServiceHost
,ServiceEndpoint
和ServiceContract
如何协同工作来创建ChannelListener。
对于实现服务的每个类,我是否需要单独的WebServiceHost
?我没有看到使用单个WebServiceHost
其次,我将接口传递给AddServceEndpoint方法,并假设该方法检查所有OperationContract成员的对象并添加它们,问题是WebServiceHost如何知道哪个类应映射到哪个接口
我喜欢的是创建WCF自托管服务的示例,该服务运行多个服务,同时保持接口和实现类分离。
答案 0 :(得分:3)
对我而言,您遇到的问题是您尝试在同一服务URI上注册多个服务。这不起作用,正如您所注意到的,每个服务必须具有唯一的端点。
独特的
实施例
http://someserver/foo -> IFoo Service
http://someserver/bar -> IBar Service
http://somedomain -> IFoo Service
http://someotherdomain -> IBar Service
http://somedomain:1 -> IFoo Service
http://somedomain:2 -> IBar Service
你明白了。
因此,要直接解决您的问题,如果您希望多个服务位于您网站的根网址,则必须将它们放在不同的端口上。因此,您可以将代码修改为
public class PortNumberAttribute : Attribute
{
public int PortNumber { get; set; }
public PortNumberAttribute(int port)
{
PortNumber = port;
}
}
[PortNumber(8085)]
public interface IEventsService
{
//service methods etc
}
string baseUri = "http://foo.com:{0}";
Type iface = typeof(IEventsService);
PortNumberAttribute pNumber = (PortNumberAttribute)iface.GetCustomAttribute(typeof(PortNumberAttribute));
Uri newUri = new Uri(string.Format(baseUri, pNumber.PortNumber));
//create host and all that
答案 1 :(得分:2)
我认为重新考虑一下你的URI方法可能对你有用。 Uri是唯一的资源标识符。 您的每个端点都表示您尝试在“事件”和“其他资源”之外的不同类型的资源之外公开。因此,您需要稍微更改UriTemplates。
我会这样做:
[ServiceContract]
public interface IEventTypesService
{
[OperationContract]
[WebGet(UriTemplate = "", ResponseFormat=WebMessageFormat.Json)]
IList<EventType> GetEventTypes();
[OperationContract]
[WebGet(UriTemplate = "{id}")]
EventType GetEventType(string id);
}
[ServiceContract]
public interface IEventsService
{
[OperationContract]
[WebGet(UriTemplate = "")]
Stream GetEventsAsStream();
[OperationContract]
[WebGet(UriTemplate = "{id}")]
Event GetEvent(string id);
}
public class EventsService: IEventsService, IEventTypesService
{
public IList<EventType> GetEventTypes() { //code in here }
public EventType GetEventType(string id) { //code in here }
public Stream GetEventsAsStream() { // code in here }
public EventType GetEventType(string id) { // code in here }
}
Type t = typeof(EventService);
Type interface1 = typeof(IEventsService);
Type interface2 = typeof(IEventTypesService);
var baseUri = new Uri("http://localhost");
Uri eventsUri= new Uri(baseUri, "Events");
Uri eventTypesUri= new Uri(baseUri, "EventTypes");
WebServicesHost host = new WebServiceHost(t, baseUri);
Binding binding = New WebHttpBinding();
host.AddServiceEndpoint(interface1, binding, eventsUri);
host.AddServiceEndpoint(interface2, binding, eventTypesUri);
是的,你是对的 - 你必须有不同的地址,但这是非常不同的资源。要更好地理解它,您可以参考:RESTful API Design,best-practices-for-a-pragmatic-restful-api
要完成,有一种方法可以使用相同的地址,但方法有点奇怪: Using the same address
答案 2 :(得分:0)
以下解决方案:
它确实需要多个WebServiceHost - 每个对象处理一个请求。另一个困难是添加更深层次的端点(例如/ events / 2014)意味着它们要么需要具有唯一参数,要么URI模板必须包含路径的一部分,如果您通过配置而不应该是一个问题。
WebServiceHost只能托管一件事(类),但该对象可以有多个接口来处理不同URL上的多种不同类型的请求。不同的WebServiceHosts如何绑定到同一个域:port?他们不能这样我猜测WebServiceHost包装了一个将请求路由到正确对象的底层静态对象。这在技术上不能回答你的问题,但我认为这个实现可以让你做你想做的事情吗?
托管网络服务的控制台应用。
public class Program
{
static void Main (string[] args)
{
var venueHost = new WebServiceHost (typeof (Venues));
venueHost.AddServiceEndpoint (typeof (IVenues), new WebHttpBinding (), "http://localhost:12345/venues");
venueHost.Open ();
var eventHost = new WebServiceHost (typeof (Events));
eventHost.AddServiceEndpoint (typeof (IEvents), new WebHttpBinding (), "http://localhost:12345/events");
eventHost.Open ();
while (true)
{
var k = Console.ReadKey ();
if (k.KeyChar == 'q' || k.KeyChar == 'Q')
break;
}
}
}
Venues类实现IVenues并处理对http://localhost:12345/venues/
[ServiceContract]
public interface IVenues
{
[WebInvoke (Method = "GET", UriTemplate = "?id={id}")]
string GetVenues (string id);
}
public class Venues : IVenues
{
public string GetVenues (string id)
{
return "This would contain venue data.";
}
}
Events类实现IEvents并处理对http://localhost:12345/events/
[ServiceContract]
public interface IEvents
{
[WebInvoke (Method = "GET", UriTemplate = "?venue={venue}")]
string GetEvents (string venue);
}
public class Events : IEvents
{
public string GetEvents (string venue)
{
return "This would contain event data.";
}
}
答案 3 :(得分:-1)
WCF自托管可以通过多种方式完成,如控制台应用程序托管,Windows服务托管等。
我曾尝试使用单一控制台应用程序托管两项服务。服务的结构与您提到的类似,即两个服务的单独类和接口。
您可能需要查看此链接: Hosting two WCf services using one console app