1

我目前正在 Windows 服务(不是 IIS)中运行一些 WCF REST 服务,使用WebServiceHost. 我为每个服务定义了一个单独的接口和类,但是我在理解如何WebServiceHostServiceEndpoint并且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

然后我重复另一个服务接口和服务类。我想删除EventsURL 中的,但是如果我这样做并WebServiceHosts使用相同的基本 URL 创建多个,我会收到错误:

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 自托管服务的示例,该服务运行多个服务,同时保持接口和实现类分开。

4

4 回答 4

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  

你明白了。

因此,为了直接解决您的问题,如果您希望不止一次的服务位于您网站的根 url,您将不得不将它们放在不同的端口上。因此,您可以将代码修改为

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
于 2014-04-11T18:15:01.850 回答
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

最后,有一种方法可以使用相同的地址,但方法有点奇怪: 使用相同的地址

于 2014-04-11T22:05:26.927 回答
0

以下解决方案:

  • 允许单个对象处理特定端点
  • 路径的任何部分都不在 URI 模板中
  • 对所有服务使用相同的端口

它确实需要一个以上的 WebServiceHost - 每个处理请求的对象一个。另一个困难是添加更深层次的端点(如 /events/2014)意味着它们要么需要具有唯一参数,要么 URI 模板必须包含路径的一部分,如果您采用约定而不是配置,这应该不是问题。

WebServiceHost 只能托管一个事物(类),但该对象可以有多个接口来处理不同 URL 上的多种不同类型的请求。不同的 WebServiceHosts 如何绑定到同一个域:端口?他们不能,所以我猜 WebServiceHost 包装了一个底层静态对象,该对象将请求路由到正确的对象。这在技术上并不能回答你的问题,但我认为这个实现可以让你做你想做的事吗?

托管 Web 服务的控制台应用程序。

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.";
        }
    }
于 2014-04-11T20:57:02.837 回答
-1

WCF 自托管可以通过多种方式完成,例如控制台应用程序托管、Windows 服务托管等。

我曾尝试使用单个控制台应用程序托管两个服务。服务的结构与您提到的类似,即两个服务的单独类和接口。

你可能想看看这个链接: Hosting two WCf services using one console app

于 2014-04-11T08:42:35.953 回答