ChatGPT解决这个技术问题 Extra ChatGPT

Getting full URL of action in ASP.NET MVC [duplicate]

This question already has answers here: How do I find the absolute url of an action in ASP.NET MVC? (9 answers) Closed 4 years ago.

Is there a built-in way of getting the full URL of an action?

I am looking for something like GetFullUrl("Action", "Controller") that would return something like http://www.fred.com/Controller/Action.

The reason I am looking for this is to avoid hardcoding URLs in automated emails that are generated so that the URLs can always be generated relative to the current location of the site.


J
Jalal

There is an overload of Url.Action that takes your desired protocol (e.g. http, https) as an argument - if you specify this, you get a fully qualified URL.

Here's an example that uses the protocol of the current request in an action method:

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

HtmlHelper (@Html) also has an overload of the ActionLink method that you can use in razor to create an anchor element, but it also requires the hostName and fragment parameters. So I'd just opt to use @Url.Action again:

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

No bother - you'd think that there should still be a better way of doing this, but hey...
The right url is https://127.0.0.1/Home/Index but it generates https://127.0.0.1:444/Home/Index. It must have no port. I'm running on Azure emulator. Please help.
@fiberOptics - rather late for you, but for others: the issue you're having is that the you're running the Azure Emulator on a non-standard port (there's usually a note about that as it starts), as such the port is required for this work. In production it should use the standard port (443) so it won't be included in the URL.
In MVC6 I did it like this <a href="@Url.Action("Login", "Account", null, Context.Request.Scheme)">Log in</a>
This doesn't work; it loses the Port. If your site is running on a non-standard port this will generate broken URLs
A
Ahmed Mansour

As Paddy mentioned: if you use an overload of UrlHelper.Action() that explicitly specifies the protocol to use, the generated URL will be absolute and fully qualified instead of being relative.

I wrote a blog post called How to build absolute action URLs using the UrlHelper class in which I suggest to write a custom extension method for the sake of readability:

/// <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);
}

You can then simply use it like that in your view:

@Url.AbsoluteAction("Action", "Controller")

Wish you got more upvotes, this is actually a no-brainer that should be in the framework IMO
The only thing i would add (or modify) is to replace the literal "http" with HttpContext.Current.Request.Url.Scheme. This will allow https to be used when appropriate.
Using the Scheme from the original URL ensure that you do not move from HTTPS to HTTP by mistake. Prevent TLS disclosure. You get my vote.
@AndreasLarsen Hmm, are you sure that this is the solution to your problem? Technically, the above extension method doesn't do anything else than calling the UrlHelper.Action method with the current HTTP request's scheme.
This is great. Thanks. Small note... Would method name "ActionAbsolute" be a better choice for sake of VS autocomplete as Url.Action() will come next to Url.ActionAbsolute().
S
Salman Zahid

This what you need to do.

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

This is the same as the accepted answer. Answers are best posted when something new is introduced.
y
youpilat13

This question is specific to ASP .NET however I am sure some of you will benefit of system agnostic javascript which is beneficial in many situations.

UPDATE: The way to get url formed outside of the page itself is well described in answers above.

Or you could do a oneliner like following:

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

from filter or:

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

However quite often one needs to get the url of current page, for those cases using Html.Action and putting he name and controller of page you are in to me feels awkward. My preference in such cases is to use JavaScript instead. This is especially good in systems that are half re-written MVT half web-forms half vb-script half God knows what - and to get URL of current page one needs to use different method every time.

One way is to use JavaScript to get URL is window.location.href another - document.URL


This would only work to present the URL of the current page. What the OP wants to do is pass the names of a .Net Controller and View to generate the URL to get to that page.
M
Matt D

I was having an issue with this, my server was running behind a load balancer. The load balancer was terminating the SSL/TLS connection. It then passed the request to the web servers using http.

Using the Url.Action() method with Request.Url.Schema, it kept creating a http url, in my case to create a link in an automated email (which my PenTester didn't like).

I may have cheated a little, but it is exactly what I needed to force a https url:

<a href="@Url.Action("Action", "Controller", new { id = Model.Id }, "https")">Click Here</a>

I actually use a web.config AppSetting so I can use http when debugging locally, but all test and prod environments use transformation to set the https value.


Request.Url.Schema uses the schema of the "current request" to determine HTTP vs HTTPS. If your anchor tag was generated by an HTTP request, then the schema won't be HTTPS. Hardcoding a secure protocol seems fine; I don't think it's "cheating" to do this, and I'm surprised the other answers didn't acknowledge that using Request.Url.Schema for a site that serves both HTTP & HTTPS resources could be an issue. App Settings / Web Config is probably the way to go if the protocol needs to be different on different environments.
J
Jorge Aguirre

This may be just me being really, really picky, but I like to only define constants once. If you use any of the approaches defined above, your action constant will be defines multiple times.

To avoid this, you can do the following:

    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();
        }
    }

Then create your controllers, say for example the 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);
    }

Then just use it like:

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

And from your .cshtml (or any code)

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

This is definitely a lot more work, but I rest easy knowing compile time validation is on my side.


Where do you store the url class and the controller class?
Wherever you want!
You could also use the nameof operator on the Action method.