Getting full URL of action in ASP.NET MVC

C#.Netasp.net MvcUrl Routing

C# Problem Overview


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.

C# Solutions


Solution 1 - C#

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>

Solution 2 - C#

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")

Solution 3 - C#

This what you need to do.

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

Solution 4 - C#

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

Solution 5 - C#

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.

Solution 6 - C#

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlan SparkView Question on Stackoverflow
Solution 1 - C#PaddyView Answer on Stackoverflow
Solution 2 - C#Marius SchulzView Answer on Stackoverflow
Solution 3 - C#Salman ZahidView Answer on Stackoverflow
Solution 4 - C#Matas VaitkeviciusView Answer on Stackoverflow
Solution 5 - C#Matt DView Answer on Stackoverflow
Solution 6 - C#Jorge AguirreView Answer on Stackoverflow