自动化处理HTTP OPTIONS请求

在Web开发中,HTTPOPTIONS请求是一种用于描述目标资源的通信选项的方法。它通常用于跨域资源共享(CORS)策略中,以确定服务器支持哪些HTTP请求方法。然而,手动维护每个URI支持的方法列表不仅繁琐,而且容易出错。本文将介绍如何使用ASP.NETWeb API自动化处理HTTP OPTIONS请求,以提高Web服务的响应效率和准确性。

基础实现

ASP.NETWeb API中,可以通过在控制器中添加一个OPTIONS动作来响应OPTIONS请求。例如:

[HttpOptions] [ResponseType(typeof(void))] [Route("Books", Name = "Options")] public IHttpActionResult Options() { HttpContext.Current.Response.AppendHeader("Allow", "GET,OPTIONS"); return Ok(); }

上述代码会在响应头中添加"Allow"字段,列出了该URI支持的HTTP方法。然而,这种方法需要为每个控制器和每个URI手动添加OPTIONS动作,且容易遗漏。

自动化方案

为了自动化处理OPTIONS请求,可以创建一个HTTP消息处理器。这个处理器继承自DelegatingHandler类,并重写SendAsync方法。这样,可以在基础路由机制运行之前添加功能。

protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { return await base.SendAsync(request, cancellationToken).ContinueWith( task => { var response = task.Result; if (request.Method == HttpMethod.Options) { var methods = new ActionSelector(request).GetSupportedMethods(); if (methods != null) { response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(string.Empty) }; response.Content.Headers.Add("Allow", methods); response.Content.Headers.Add("Allow", "OPTIONS"); } } return response; }, cancellationToken); }

在上面的代码中,首先检查请求的方法是否为OPTIONS。如果是,使用ActionSelector类来获取支持的方法列表,并将其添加到响应头中。

ActionSelector类

ActionSelector类负责根据请求找到合适的控制器,并确定支持的方法。如果找不到控制器,GetSupportedMethods方法将返回null。IsMethodSupported方法通过创建一个新的请求并检查是否有相应的动作来确定是否支持该方法。

private class ActionSelector { private readonly HttpRequestMessage _request; private readonly HttpControllerContext _context; private readonly ApiControllerActionSelector _apiSelector; private static readonly string[] Methods = { "GET", "PUT", "POST", "PATCH", "DELETE", "HEAD", "TRACE" }; public ActionSelector(HttpRequestMessage request) { try { var configuration = request.GetConfiguration(); var requestContext = request.GetRequestContext(); var controllerDescriptor = new DefaultHttpControllerSelector(configuration).SelectController(request); _context = new HttpControllerContext { Request = request, RequestContext = requestContext, Configuration = configuration, ControllerDescriptor = controllerDescriptor }; } catch { return; } _request = _context.Request; _apiSelector = new ApiControllerActionSelector(); } public IEnumerable<string> GetSupportedMethods() { return _request == null ? null : Methods.Where(IsMethodSupported); } private bool IsMethodSupported(string method) { _context.Request = new HttpRequestMessage(new HttpMethod(method), _request.RequestUri); var routeData = _context.RouteData; try { return _apiSelector.SelectAction(_context) != null; } catch { return false; } finally { _context.RouteData = routeData; } } }

在上述代码中,首先尝试找到请求对应的控制器。如果找不到,返回null。然后,检查请求的方法是否被支持。

添加消息处理器

最后一步是在启动代码中添加消息处理器。这样,REST服务将为每个有效的URI创建OPTIONS响应。

configuration.MessageHandlers.Add(new OptionsHandler());

通过这种方式,可以自动化处理HTTP OPTIONS请求,提高Web服务的响应效率和准确性。

注意事项

虽然上述方法可以自动化处理OPTIONS请求,但还有一些问题需要注意:

  • 处理器没有考虑授权问题。
  • 即使URI包含错误的资源ID,服务也会响应请求,例如/books/0。

为了解决这些问题,需要为每个包含资源ID的URI添加控制器动作OPTIONS,因为只有控制器可以授权资源。同时,处理器应该使用动作的响应(如果有的话)。

protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { return await base.SendAsync(request, cancellationToken).ContinueWith( task => { var response = task.Result; switch (GetResponseAction(request, response)) { case ResponseAction.UseOriginal: return response; case ResponseAction.ReturnUnauthorized: return new HttpResponseMessage(HttpStatusCode.Unauthorized); case ResponseAction.ReturnUnauthorized: var methods = new ActionSelector(request).GetSupportedMethods(); if (methods == null) return response; response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(string.Empty) }; response.Content.Headers.Add("Allow", methods); response.Content.Headers.Add("Allow", "OPTIONS"); return response; default: throw new InvalidOperationException("Unsupported response action code"); } }, cancellationToken); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485