2012-07-13 55 views
4

我正在使用服務堆棧創建自託管REST服務& AppHostHttpListenerBase。我想用我的服務基本URI(例如「API」),像這樣:配置ServiceStack基本URI

http://myserver/api/service1/param 
http://myserver/api/service2/param 

如何做到這一點無需在每個我的路線定義「API」。在IIS中,我可以設置一個虛擬目錄來隔離服務,但如何在自託管時執行此操作?

回答

2

ServiceStack's HttpListener hosts預計將託管根/路徑,因爲正常使用情況是讓每個自託管服務在不同的自定義端口上可用。

由於它目前不支持在/ custompath中託管,因此您必須在所有服務路由上指定/api/前綴。

Add an issue如果您想查看自定義路徑的託管支持。

1

我找到了解決方法。我只在自我託管下測試過。

創建從RouteAttribute

繼承
public class PrefixedRouteAttribute : RouteAttribute 
{ 
    public static string Prefix { get; set; } 

    public PrefixedRouteAttribute(string path) : 
    base(path) 
    { 
    SetPrefix(); 
    } 

    public PrefixedRouteAttribute(string path, string verbs) 
    : base(path, verbs) 
    { 
    SetPrefix(); 
    } 

    private void SetPrefix() 
    { 
    if (!string.IsNullOrWhiteSpace(Prefix)) 
    { 
     this.Path = string.Format("/{0}{1}", Prefix, Path); 
    } 
    } 
} 

一個「PrefixedRouteAttribute」類當您創建APPHOST你可以設置你的前綴

PrefixedRouteAttribute.Prefix = "api"; 

然後,而不是使用[路徑]屬性,使用[PrefixRoute]屬性

[PrefixedRoute("/echo")] 
[PrefixedRoute("/echo/{Value*}")] 
public class Echo 
{ 
    [DataMember] 
    public string Value { get; set; } 
} 

This wi然後工作請求

/api/echo 
/api/echo/1 

這可能可以改進。我不太喜歡我需要通過靜態屬性設置前綴,但我無法想象在我的設置下更好的方法。雖然創建重寫屬性的原理聽起來很合理,但這是重要的一部分。

+0

不錯的主意......我們最終結束了放棄自託管的IIS提供了更多的高級用例的選項(健康監控,deamonizing等)。 FWIW ...我通過流暢的界面註冊我的路線PrefixedRoute不會工作無副作用AppHostBase ...但仍然,很好的方法和有趣的,簡單的解決方案。 – Fred 2012-11-30 19:08:15

2

實際上有一個更簡單的解決方案。在你的web.config,更新您的HTTP處理程序:

<httpHandlers> 
    <add path="api*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" /> 
</httpHandlers> 

通過上述,您的所有服務的API必須用「/ API /」爲前綴。如果您已經在任何路由中使用了「/ api /」,則現在必須刪除它們,或者必須在呼叫中指定兩次。

參考: https://github.com/ServiceStack/SocialBootstrapApi

+0

問題在於將自託管作爲Windows服務。這是爲了在IIS下託管。 – Fred 2013-04-03 17:32:31

+0

我也選擇了這個開放。 – James 2013-11-19 10:15:57

4

這裏亞去..(作爲獎勵,這是你如何把你的服務變成一個插件

using BlogEngineService; 
using ServiceStack.WebHost.Endpoints; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace BlogEngineWinService 
{ 
    public class AppHost : AppHostHttpListenerBase 
    { 
     public AppHost() : base("Self Host Service", typeof(AppHost).Assembly) { } 
     public override void Configure(Funq.Container container) 
     { 
      Plugins.Add(new BlogEngine()); 
     } 
    } 
} 

這是你如何自動裝配起來

電話appHost.Routes.AddFromAssembly2(typeof(HelloService).Assembly);是什麼叫擴展到汽車線。

using ServiceStack.WebHost.Endpoints; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using ServiceStack.ServiceInterface; 

namespace BlogEngineService 
{ 
    public class BlogEngine : IPlugin, IPreInitPlugin 
    { 
     public void Register(IAppHost appHost) 
     { 

      appHost.RegisterService<HelloService>(); 
      appHost.Routes.AddFromAssembly2(typeof(HelloService).Assembly); 
     } 

     public void Configure(IAppHost appHost) 
     { 

     } 
    } 
} 

這就是你如何標記服務類給它一個前綴。 只要選中類具有這種屬性

using ServiceStack.DataAnnotations; 
using ServiceStack.ServiceHost; 
using ServiceStack.ServiceInterface; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace BlogEngineService 
{ 
    public class Hello 
    { 
     [PrimaryKey] 
     public string Bob { get; set; } 
    } 

    public class HelloResponse 
    { 
     public string Result { get; set; } 
    } 

    [PrefixedRoute("/test")] 
    public class HelloService : Service 
    { 

     public object Any(Hello request) 
     { 
      return new HelloResponse { Result = "Hello, " + request.Bob}; 
     } 
    } 
} 

在項目中創建爲擴展名的文件CS ..

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 
using ServiceStack.Common; 
using ServiceStack.Common.Utils; 
using ServiceStack.Common.Web; 
using ServiceStack.Text; 
using ServiceStack.ServiceHost; 
using ServiceStack.WebHost.Endpoints; 
using ServiceStack.ServiceInterface; 

namespace ServiceStack.ServiceInterface 
{ 
    public static class ServiceRoutesExtensions 
    { 
     /// <summary> 
     ///  Scans the supplied Assemblies to infer REST paths and HTTP verbs. 
     /// </summary> 
     ///<param name="routes">The <see cref="IServiceRoutes"/> instance.</param> 
     ///<param name="assembliesWithServices"> 
     ///  The assemblies with REST services. 
     /// </param> 
     /// <returns>The same <see cref="IServiceRoutes"/> instance; 
     ///  never <see langword="null"/>.</returns> 
     public static IServiceRoutes AddFromAssembly2(this IServiceRoutes routes, 
                params Assembly[] assembliesWithServices) 
     { 
      foreach (Assembly assembly in assembliesWithServices) 
      { 

       AddNewApiRoutes(routes, assembly); 
      } 

      return routes; 
     } 

     private static void AddNewApiRoutes(IServiceRoutes routes, Assembly assembly) 
     { 
      var services = assembly.GetExportedTypes() 
       .Where(t => !t.IsAbstract 
          && t.HasInterface(typeof(IService))); 

      foreach (Type service in services) 
      { 
       var allServiceActions = service.GetActions(); 
       foreach (var requestDtoActions in allServiceActions.GroupBy(x => x.GetParameters()[0].ParameterType)) 
       { 
        var requestType = requestDtoActions.Key; 
        var hasWildcard = requestDtoActions.Any(x => x.Name.EqualsIgnoreCase(ActionContext.AnyAction)); 
        string allowedVerbs = null; //null == All Routes 
        if (!hasWildcard) 
        { 
         var allowedMethods = new List<string>(); 
         foreach (var action in requestDtoActions) 
         { 
          allowedMethods.Add(action.Name.ToUpper()); 
         } 

         if (allowedMethods.Count == 0) continue; 
         allowedVerbs = string.Join(" ", allowedMethods.ToArray()); 
        } 
        if (service.HasAttribute<PrefixedRouteAttribute>()) 
        { 
         string prefix = ""; 
         PrefixedRouteAttribute a = (PrefixedRouteAttribute)Attribute.GetCustomAttribute(service, typeof(PrefixedRouteAttribute)); 
         if (a.HasPrefix()) 
         { 
          prefix = a.GetPrefix(); 
         } 
         routes.AddRoute(requestType, allowedVerbs, prefix); 
        } 
        else 
        { 
         routes.AddRoute(requestType, allowedVerbs); 
        } 
       } 
      } 
     } 

     private static void AddRoute(this IServiceRoutes routes, Type requestType, string allowedVerbs, string prefix = "") 
     { 
      var newRoutes = new ServiceStack.ServiceHost.ServiceRoutes(); 
      foreach (var strategy in EndpointHost.Config.RouteNamingConventions) 
      { 
       strategy(newRoutes, requestType, allowedVerbs); 
      } 
      foreach (var item in newRoutes.RestPaths) 
      { 

       string path = item.Path; 
       if (!string.IsNullOrWhiteSpace(prefix)) 
       { 
        path = prefix + path; 
       } 
       routes.Add(requestType, restPath: path, verbs: allowedVerbs); 
      } 
     } 
    } 

    public class PrefixedRouteAttribute : Attribute 
    { 
     private string _prefix { get; set; } 
     private bool _hasPrefix { get; set; } 

     public PrefixedRouteAttribute(string path) 
     { 
      if (!string.IsNullOrWhiteSpace(path)) 
      { 
       this._hasPrefix = true; 
       this._prefix = path; 
       //this.Path = string.Format("/{0}{1}", Prefix, Path); 
      } 
     } 
     public bool HasPrefix() 
     { 
      return this._hasPrefix; 
     } 
     public string GetPrefix() 
     { 
      return this._prefix; 
     } 
    } 
}