在ASP.NET MVC中获取操作的完整URL

413

如何获取操作的完整 URL?

我正在寻找类似于 GetFullUrl("Action", "Controller") 这样的方法,以返回类似于 http://www.fred.com/Controller/Action 的 URL。

我之所以要这样做,是为了避免在生成自动化电子邮件中硬编码 URL,以便 URL 可以始终相对于站点的当前位置生成。


参见:https://dev59.com/MXRC5IYBdhLWcg3wCMc6 - Andrew
6个回答

624

有一个Url.Action重载函数,可以把你所需的协议(如http、https)作为参数传入,如果你指定了它,就可以得到一个完全合格的URL。

以下是一个示例,它在操作方法中使用当前请求的协议:

var fullUrl = this.Url.Action("Edit", "Posts", new { id = 5 }, this.Request.Url.Scheme);

HtmlHelper (@Html) 在 Razor 中还有一个 ActionLink 方法的重载,可以用来创建锚点元素,但是它还需要 hostName 和 fragment 参数。所以我会选择再次使用 @Url.Action:

<span>
  Copy
  <a href='@Url.Action("About", "Home", null, Request.Url.Scheme)'>this link</a> 
  and post it anywhere on the internet!
</span>

8
没关系 - 你可能认为还应该有更好的方法来做到这一点,但是嘿... - Paddy
1
正确的URL是https://127.0.0.1/Home/Index,但它生成了https://127.0.0.1:444/Home/Index。它不能有端口。我正在运行Azure模拟器,请帮忙。 - fiberOptics
2
@fiberOptics - 对于你来说可能有点晚了,但对于其他人来说:你遇到的问题是因为你在非标准端口上运行Azure模拟器(通常在启动时会有相关提示),因此该端口是必需的。在生产环境中,应使用标准端口(443),因此它不会包含在URL中。 - Zhaph - Ben Duguid
12
在MVC6中,我做了这样的内容 <a href="@Url.Action("Login", "Account", null, Context.Request.Scheme)">登录</a> - Chris
1
这个不起作用,它会丢失端口。如果您的网站运行在非标准端口上,这将生成损坏的URL。 - Orion Edwards
显示剩余3条评论

142

正如Paddy所提到的:如果你使用UrlHelper.Action()的一个重载并明确指定要使用的协议,生成的URL将是绝对和完全限定的,而不是相对的。

我写了一篇名为“如何使用UrlHelper类构建绝对操作URL的”博客文章,其中建议编写自定义扩展方法以提高可读性:

/// <summary>
/// Generates a fully qualified URL to an action method by using
/// the specified action name, controller name and route values.
/// </summary>
/// <param name="url">The URL helper.</param>
/// <param name="actionName">The name of the action method.</param>
/// <param name="controllerName">The name of the controller.</param>
/// <param name="routeValues">The route values.</param>
/// <returns>The absolute URL.</returns>
public static string AbsoluteAction(this UrlHelper url,
    string actionName, string controllerName, object routeValues = null)
{
    string scheme = url.RequestContext.HttpContext.Request.Url.Scheme;

    return url.Action(actionName, controllerName, routeValues, scheme);
}
你可以在你的视图中像这样简单地使用它:
@Url.AbsoluteAction("Action", "Controller")

9
我会尽力为您提供准确的翻译:唯一需要添加(或修改)的是将文本中的字面“http”替换为HttpContext.Current.Request.Url.Scheme。这将使得适当时可以使用https。 - Jay
1
使用来自原始URL的Scheme确保不要错误地从HTTPS转到HTTP。防止TLS泄露。你得到了我的投票。 - Pierre-Alain Vigeant
1
@AndreasLarsen 嗯,你确定这是你问题的解决方案吗?技术上,上述扩展方法除了使用当前HTTP请求的方案调用UrlHelper.Action方法之外,没有其他操作。 - Marius Schulz
2
这很好。谢谢。小提示... 为了VS自动完成的缘故,方法名称“ActionAbsolute”是否是更好的选择,因为Url.Action()将紧随其后Url.ActionAbsolute()。 - Max
[mvc新手] 我应该在哪里使用这个方法?是在我的某个自定义类中还是在控制器中? - heyNow
显示剩余4条评论

4

这是你需要做的事情。

@Url.Action(action,controller, null, Request.Url.Scheme)

8
这与被接受的答案相同。最好在引入新内容时发布答案。 - IAmGroot

2

这个问题是特定于ASP .NET的,但我相信一些人会从系统无关的JavaScript中受益,这在许多情况下都很有用。

更新:获取页面外部形成的URL的方法在上面的答案中得到了很好的描述。

或者您可以像以下一行代码一样简单解决:

new UrlHelper(actionExecutingContext.RequestContext).Action(
    "SessionTimeout", "Home", 
    new {area = string.Empty}, 
    actionExecutingContext.Request.Url!= null? 
    actionExecutingContext.Request.Url.Scheme : "http"
);

从过滤器或:

new UrlHelper(this.Request.RequestContext).Action(
    "Details", 
    "Journey", 
    new { area = productType }, 
    this.Request.Url!= null? this.Request.Url.Scheme : "http"
);

然而,经常需要获取当前页面的URL地址,在这种情况下,使用Html.Action并将所在页面的名称和控制器放入其中会感到很尴尬。在这种情况下,我更喜欢使用JavaScript。特别是在系统中,它们是半重写MVT半Web表单半VB脚本半神知道什么 - 要获取当前页面的URL,每次都需要使用不同的方法。

一种使用JavaScript获取URL的方法是window.location.href,另一种是document.URL


1
这只能用来展示当前页面的URL。OP想要做的是传递 .Net 控制器和视图的名称以生成到达该页面的URL。 - Matthew Verstraete

1
我遇到了一个问题,我的服务器在负载均衡器后面运行。负载均衡器终止了SSL/TLS连接,然后使用http将请求传递给Web服务器。
使用Url.Action()方法和Request.Url.Schema,在创建自动化电子邮件中的链接时,它一直创建http网址(我的PenTester不喜欢)。
我可能有点作弊,但这正是我需要的来强制使用https网址:
<a href="@Url.Action("Action", "Controller", new { id = Model.Id }, "https")">Click Here</a>

实际上,我使用了web.config的AppSetting,这样我就可以在本地调试时使用http,但是所有的测试和生产环境都使用转换来设置https值。


Request.Url.Schema 使用“当前请求”的模式来确定 HTTP vs HTTPS。如果您的锚标签是由 HTTP 请求生成的,则模式不会是 HTTPS。硬编码安全协议似乎没问题;我认为这样做并不是“作弊”,而且我很惊讶其他答案没有承认在为同时提供 HTTP 和 HTTPS 资源的站点使用 Request.Url.Schema 可能会有问题。如果需要在不同的环境中使用不同的协议,则 App Settings / Web Config 可能是最好的选择。 - Lovethenakedgun

0
这可能只是我非常挑剔,但我喜欢只定义一次常量。如果您使用上面定义的任何方法,则您的操作常量将被多次定义。
为避免这种情况,您可以执行以下操作:
    public class Url
    {
        public string LocalUrl { get; }

        public Url(string localUrl)
        {
            LocalUrl = localUrl;
        }

        public override string ToString()
        {
            return LocalUrl;
        }
    }

    public abstract class Controller
    {
        public Url RootAction => new Url(GetUrl());

        protected abstract string Root { get; }

        public Url BuildAction(string actionName)
        {
            var localUrl = GetUrl() + "/" + actionName;
            return new Url(localUrl);
        }

        private string GetUrl()
        {
            if (Root == "")
            {
                return "";
            }

            return "/" + Root;
        }

        public override string ToString()
        {
            return GetUrl();
        }
    }

然后创建你的控制器,比如说DataController:

    public static readonly DataController Data = new DataController();
    public class DataController : Controller
    {
        public const string DogAction = "dog";
        public const string CatAction = "cat";
        public const string TurtleAction = "turtle";

        protected override string Root => "data";

        public Url Dog => BuildAction(DogAction);
        public Url Cat => BuildAction(CatAction);
        public Url Turtle => BuildAction(TurtleAction);
    }

然后就像这样使用:

    // GET: Data/Cat
    [ActionName(ControllerRoutes.DataController.CatAction)]
    public ActionResult Etisys()
    {
        return View();
    }

从你的.cshtml文件(或任何代码)中

<ul>
    <li><a href="@ControllerRoutes.Data.Dog">Dog</a></li>
    <li><a href="@ControllerRoutes.Data.Cat">Cat</a></li>
</ul>

这肯定需要更多的工作,但我放心,因为编译时验证站在我的一边。


你把URL类和控制器类存储在哪里? - Mo D Genesis
无论你想放在哪里! - Jorge Aguirre
您还可以在Action方法上使用nameof运算符。 - Arjen de Mooij

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接