我使用CorsFeature进行了服务设置,并使用mythz在其他答案中建议的方法,收集在appHost文件中使用的函数中:
private void ConfigureCors(Funq.Container container)
{
Plugins.Add(new CorsFeature(allowedOrigins: "*",
allowedMethods: "GET, POST, PUT, DELETE, OPTIONS",
allowedHeaders: "Content-Type, Authorization, Accept",
allowCredentials: true));
PreRequestFilters.Add((httpReq, httpRes) =>
{
//Handles Request and closes Responses after emitting global HTTP Headers
if (httpReq.HttpMethod == "OPTIONS")
{
httpRes.EndRequest();
}
});
}
但是,预请求过滤器仅针对某些服务请求触发。我们在服务中拥有的一个基本实体是一个问题实体,并且有如下定义的自定义路由:
[Route("/question")]
[Route("/question/{ReviewQuestionId}", "GET,DELETE")]
[Route("/question/{ReviewQuestionId}/{ReviewSectionId}", "GET")]
使用POSTMAN触发测试查询(全部使用OPTIONS动词),我们可以看到这将触发预请求过滤器:
http://localhost/myservice/api/question/
但这不会:
http://localhost/myservice/api/question/66
据推测,这是因为第二和第三条路线明确定义了它们接受的动词,OPTIONS不是其中之一。
是否真的有必要在限制所支持的动词的每个定义路线中拼出OPTIONS?
答案 0 :(得分:7)
PreRequestFilters仅针对不排除OPTIONS的有效路线触发(例如,通过离开Verbs=null
并允许其处理所有动词 - inc.OPTIONS)。
为了能够处理所有OPTIONS请求(即使对于非匹配路由),您需要在start of the Request pipeline处(即在匹配路由之前)处理Config.RawHttpHandlers
的请求。这是在CorsFeature中为您在ServiceStack的下一个主要(v4)版本中完成的:
//Handles Request and closes Response after emitting global HTTP Headers
var emitGlobalHeadersHandler = new CustomActionHandler(
(httpReq, httpRes) => httpRes.EndRequest());
appHost.RawHttpHandlers.Add(httpReq =>
httpReq.HttpMethod == HttpMethods.Options
? emitGlobalHeadersHandler
: null);
CustomActionHandler并不存在于v3中,但它可以通过以下方式轻松创建:
public class CustomActionHandler : IServiceStackHttpHandler, IHttpHandler
{
public Action<IHttpRequest, IHttpResponse> Action { get; set; }
public CustomActionHandler(Action<IHttpRequest, IHttpResponse> action)
{
if (action == null)
throw new Exception("Action was not supplied to ActionHandler");
Action = action;
}
public void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
{
Action(httpReq, httpRes);
}
public void ProcessRequest(HttpContext context)
{
ProcessRequest(context.Request.ToRequest(GetType().Name),
context.Response.ToResponse(),
GetType().Name);
}
public bool IsReusable
{
get { return false; }
}
}
匹配所有路由的另一种方法是指定FallbackRoute,例如处理所有路由,您可以使用以下方法将通配符添加到Fallback路由:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
}
但由于它匹配所有未处理的路由,因此不再为非匹配请求提供404,因为现在匹配所有未匹配的路由。但您可以使用以下方法轻松处理:
public class FallbackService : Service
{
public object Any(Fallback request)
{
if (base.Request.HttpMethod == "OPTIONS")
return null;
throw HttpError.NotFound("{0} was not found".Fmt(request.Path));
}
}
答案 1 :(得分:2)
您不必将OPTIONS
动词添加到所有路线。相反,您可以执行以下操作:
只需将此路线放在Question
课程上:
[Route("/question/{*}", Verbs = "OPTIONS")]
public class Question
{
}
然后将此添加到您的问题服务类:
public void Options(Question question)
{
}
现在任何以/question/
开头的路线都会支持OPTIONS
动词。
如果您要使用/question/something/
之类的子路由,可能需要对此进行限制。
答案 2 :(得分:0)
以下步骤在ServiceStackV3中为我工作。
<强> 1。添加了一个新类CustomActionHandler
using ServiceStack.ServiceHost;
using ServiceStack.WebHost.Endpoints.Extensions;
using ServiceStack.WebHost.Endpoints.Support;
public class CustomActionHandler : IServiceStackHttpHandler, IHttpHandler
{
public Action<IHttpRequest, IHttpResponse> Action { get; set; }
public CustomActionHandler(Action<IHttpRequest, IHttpResponse> action)
{
if (action == null)
throw new Exception("Action was not supplied to ActionHandler");
Action = action;
}
public void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
{
Action(httpReq, httpRes);
}
public void ProcessRequest(HttpContext context)
{
ProcessRequest(context.Request.ToRequest(GetType().Name),
context.Response.ToResponse(),
GetType().Name);
}
public bool IsReusable
{
get { return false; }
}
}
<强> 2。在AppHostBase.Config.RawHttpHandlers集合中添加CustomHandler(此语句可以在Configure(容器容器)方法中编写)。
// Handles Request and closes Response after emitting global HTTP Headers
var emitGlobalHeadersHandler = new CustomActionHandler((httpReq, httpRes) => httpRes.EndRequest());
Config.RawHttpHandlers.Add(httpReq => httpReq.HttpMethod == HttpMethods.Options ? emitGlobalHeadersHandler : null);