我最近有一个原因需要将一个服务栈服务从.NET Core 1.1升级到.NET Core 2.0。ServiceStack和.NET Core的根URL 2
以前,我的根在URL节目类有点像这样定义...
IWebHost host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup<Startup>() .UseUrls("http://*:44444/api/myservice") .Build(); host.Run();
在.NET核2.0,看来这是不可能指定的根路径'UseUrls'方法 - 我得到一个异常,说System.InvalidOperationException: A path base can only be configured using IApplicationBuilder.UsePathBase()
使用合并UsePathBaseMiddleware为了设置根路径。
我发现,当我在WebHostBuilder中指定根路径+端口号并在Apphost文件中设置UsePathBase(在调用app.UseServiceStack(...)
之前或之后),一切都可以从根(即我认为我对UsePathBase的调用被忽略?!)。
我的请求都装饰有一个路由属性,它看起来像这样:
[Route("users/{username}", "Get"]
使用.NET 1.1的核心,我能够在
http://[URL]:[PORT]/api/user/users/[USERNAME]
使用.NET访问此服务核心2.0,服务出现在
http://[URL]:[PORT]/users/[USERNAME]
现在,我可以在每个定义的路由属性上硬编码路由以包含'/ api/user'前缀,或者我认为我可以在AppHost的GetRouteAttributes()中执行某些操作来覆盖所有发现的路由并根据需要应用前缀 - 但元数据页面总是出现在根地址(即http://[URL]:[PORT]/metadata
)而不是http://[URL]:[PORT]/api/[SERVICE]/metadata
。
这对我来说是一个问题,因为我在同一个公共URL(该端口被API网关隐藏)中有几十个服务,所以我需要元数据页面出现在除根之外的其他位置。
有没有一个(最好低影响)的方式来使服务路线的行为像在.NET Core 1.1中一样?
更新 - 18/09/17一点研究
我已经能够找到使这项工作的两种方式后(他们都不理想......)
第一种方式:
这是我的第一个解决方案的完整回购。我想通了如何使用app.UsePathBase
更改为根URL,但元数据的详细信息页面不走这条路的基础考虑,因此他们只显示每一个服务的方法从“/”
using Funq;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ServiceStack;
using System;
using System.Threading.Tasks;
namespace ServiceStackCore1Test
{
class Program
{
static void Main(string[] args)
{
Console.Title = "My Service";
IWebHost host = WebHost.CreateDefaultBuilder()
.UseStartup<Startup>()
.UseUrls("http://*:32505/")
.Build();
host.Run();
}
}
internal class PathSetupStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UsePathBase("/api/myservice");
next(app);
};
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging();
services.AddTransient<IStartupFilter, PathSetupStartupFilter>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole((x, y) => y > LogLevel.Trace);
app.UseServiceStack(Activator.CreateInstance<AppHost>());
app.Run(context => Task.FromResult(0) as Task);
}
}
public class AppHost : AppHostBase
{
public AppHost()
: base("ASM Cloud - My Service", typeof(MyService).GetAssembly())
{
}
/// <summary>
/// Configure the given container with the
/// registrations provided by the funqlet.
/// </summary>
/// <param name="container">Container to register.</param>
public override void Configure(Container container)
{
this.Plugins.Add(new PostmanFeature());
}
}
public class MyService : Service
{
public TestResponse Any(TestRequest request)
{
//throw new HttpError(System.Net.HttpStatusCode.NotFound, "SomeErrorCode");
return new TestResponse { StatusCode = 218, UserName = request.UserName };
}
[Route("/test/{UserName}", "GET", Summary = "test")]
public class TestRequest : IReturn<TestResponse>
{
public string UserName { get; set; }
}
public class TestResponse : IHasStatusCode
{
public int StatusCode { get; set; }
public string UserName { get; set; }
}
}
}
第二种方式开始 - 这确实提供了正确的功能 - 用于服务路由和元数据显示,但Servicestack在每次调用时解析路径时都会引发异常。在这个完整的repo中,我使用HandlerFactoryPath来设置我的基本URI,根据servicestack文档应该指定应用程序的基本路由。
using Funq;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ServiceStack;
using System;
using System.Threading.Tasks;
namespace ServiceStackCore1Test
{
class Program
{
static void Main(string[] args)
{
Console.Title = "My Service";
IWebHost host = WebHost.CreateDefaultBuilder()
.UseStartup<Startup>()
.UseUrls("http://*:32505/")
.Build();
host.Run();
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole((x, y) => y > LogLevel.Trace);
app.UseServiceStack(Activator.CreateInstance<AppHost>());
app.Run(context => Task.FromResult(0) as Task);
}
}
public class AppHost : AppHostBase
{
public AppHost()
: base("My Service", typeof(MyService).GetAssembly())
{
}
/// <summary>
/// Configure the given container with the
/// registrations provided by the funqlet.
/// </summary>
/// <param name="container">Container to register.</param>
public override void Configure(Container container)
{
Plugins.Add(new PostmanFeature());
SetConfig(new HostConfig
{
HandlerFactoryPath = "/api/myservice"
});
}
}
public class MyService : Service
{
public TestResponse Any(TestRequest request)
{
return new TestResponse { StatusCode = 200, UserName = request.UserName };
}
[Route("/test/{UserName}", "GET", Summary = "test")]
public class TestRequest : IReturn<TestResponse>
{
public string UserName { get; set; }
}
public class TestResponse : IHasStatusCode
{
public int StatusCode { get; set; }
public string UserName { get; set; }
}
}
}
所以,就像我说的,这个解决方案的工作,但会抛出异常
失败:Microsoft.AspNetCore.Server.Kestrel [13] 连接ID “0HL7UFC5AIAO6”,请求ID“0HL7UFC5AIAO6 :00000004“:应用程序抛出未处理的异常。 System.ArgumentOutOfRangeException:startIndex不能大于字符串的长度。 参数名称:startIndex at System.String.Substring(Int32 startIndex,Int32 length) at ServiceStack.AppHostBase.d__7.MoveNext()in C:\ BuildAgent \ work \ 799c742886e82e6 \ src \ ServiceStack \ AppHostBase.NetCore.cs:线101 ---从先前的位置堆栈跟踪,其中引发异常--- 结束在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务task) 在Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.d__3.MoveNext() ---从以前位置抛出异常的堆栈跟踪结束--- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务task) 在Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Frame
1.<ProcessRequestsAsync>d__2.MoveNext() fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HL7UFC5AIAO6", Request id "0HL7UFC5AIAO6:00000004": An unhandled exception was thrown by the application. System.ArgumentOutOfRangeException: startIndex cannot be larger than length of string. Parameter name: startIndex at System.String.Substring(Int32 startIndex, Int32 length) at ServiceStack.AppHostBase.<ProcessRequest>d__7.MoveNext() in C:\BuildAgent\work\799c742886e82e6\src\ServiceStack\AppHostBase.NetCore.cs:line 101 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Frame
1.d__2.MoveNext()一边
发现,实际的问题(不是我在更新上面显示的东西)可以降低到以下两个问题与ASPNET托管:
https://github.com/aspnet/Hosting/issues/815
https://github.com/aspnet/Hosting/issues/1120
我仍然有点无所适从,的该怎么做解决我原来的问题 - 所以我会很感激任何帮助。
是的,我知道这件事,因为我的一位同事在SS论坛上发布了这个消息 - 感谢您添加此帖的链接。这不是真正解决问题的答案。我想如果有足够多的人对这个问题感兴趣,那么他们会做一些狡猾的事来解决问题。不幸的是我不能将这个标记为答案 - 但是对我有+1 +1 – Jay