根据 URL 确定端点的 HTTP 动词

Determine the HTTP verb of an endpoint based on the URL

对于我过去从事的一个项目,我们将 URL 存储在应用程序数据库的一个字段中,这样当用户不得不离开、关闭他们的系统或其他任何事情时,他们可以通过打开受影响的记录来恢复处理某事。 此 URL 指的是在我们公开的 Web API 中为相关应用程序定义的控制器方法,其中提供了打开受影响记录所需的参数。

有一个问题,我们注意到 - 很少 - 正在存储的端点指的是 POST 端点,而不是 GET 端点。触发此条件时,我们会得到如下异常:

The parameters dictionary contains a null entry for parameter 'MyParameter' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult SomeEndpoint(Int32, Int32)' in 'MyApplication'.  An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters

我们知道我们想做什么来解决问题:我们想扫描 URL 并确定动词。如果动词是 POST,我们希望重定向到特定的 URL,以防止我们的应用程序陷入这种容易出错的状态。我们希望将此重定向设置为在服务器端发生,这不仅是出于安全原因,而且因为它根本不是客户端代码问题。

我粗略地 Google 搜索了这个主题,但没有找到任何文章讨论如何对给定 URL 的动词进行逆向工程。

问题: 给定一些 URL,有什么方法可以确定在 C# 中引用的 HTTP 动词?

如果有 not - 我期望的东西,很可能是出于安全原因 - 是否有关于避免此类问题的技术建议?在发布这个问题之前,我已经对受影响的应用程序进行了代码审计,并且无法轻易地重现导致这个问题的情况(在我们的数据库中存储 POST URL),并且没有发现应该导致 POST URL 被保存到我们的数据库的逻辑。

任何 URL 都可以与任何 HTTP 动词一起使用,包括 GET、POST、PUT、DELETE 等。因此,仅给定 URL.

无法推断动词

即使您能弄清楚动词何时是 POST,URL 也不足以重新创建页面,因为 POST 需要包含在正文中的信息(不是请求的 URL)。

听起来您的应用程序提供了某种保存在服务器端的书签功能。如果目的是让用户从他们离开的地方继续,您有几个选择。

  1. 不是仅存储 URL,而是存储完整的 HTTP 请求,包括动词和表单正文。如果任何值是特定于会话的(例如会话 cookie),那么您将需要能够以某种方式用新值替换这些值。对此要非常小心,因为可能会出现无法预料的后果,例如如果 POST 操作做了一些不能重复的事情。此外,存储涉及身份验证的任何内容的 POST 请求是一个非常糟糕的主意,例如登录页面。

  2. 不是将用户带回 POST 的目标,而是将用户带回发起 POST 的页面。一种方法是对所有处理 POST 的控制器进行编程以验证表单变量;如果它们完全丢失,则重定向到用户应该填写的页面。

  3. 考虑让用户管理他或她自己的书签,使用浏览器功能而不是服务器逻辑。

因此,如果您像我们一样使用某种动作过滤器:

public class SomeActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        CacheCurrentStep(filterContext.HttpContext.Request);
    }

    private static void CacheCurrentStep(HttpRequestBase request)
    {
        string url = request.Url.PathAndQuery;
        // Save it, however you do that
    }
}

...嗅探请求获取代理动词是可以的!

private static void CacheCurrentStep(HttpRequestBase request)
{
    if(request.HttpMethod.ToUpper() != "GET")
        return;

    string url = request.Url.PathAndQuery;
    // Save it, however you do that
}

HttpRequest class 公开了 HttpMethod 属性 returns HEAD、GET 或 POST。在 HttpRequest 上有一个相关的 属性,RequestType,只有 returns GET 或 POST,可以用来代替。

对于从未使用过动作过滤器的任何未来读者,您只需使用属性注释控制器...

[SomeAction]
public class MyApiController : ApiController
{
    // Endpoints...
}

...你已经设置好了。