Call UrlHelper in models in ASP.NET MVC
Asked Answered
E

8

166

I need to generate some URLs in a model in ASP.NET MVC. I'd like to call something like UrlHelper.Action() which uses the routes to generate the URL. I don't mind filling the usual blanks, like the hostname, scheme and so on.

Is there any method I can call for that? Is there a way to construct an UrlHelper?

Excited answered 9/1, 2010 at 1:48 Comment(1)
I was thinking about this myself, but do be aware that Url.Action will generate a relative URL. Be sure that that's what you want.Blackshear
E
70

I like Omar's answer but that's not working for me. Just for the record this is the solution I'm using now:

var httpContext = HttpContext.Current;

if (httpContext == null) {
  var request = new HttpRequest("/", "http://example.com", "");
  var response = new HttpResponse(new StringWriter());
  httpContext = new HttpContext(request, response);
}

var httpContextBase = new HttpContextWrapper(httpContext);
var routeData = new RouteData();
var requestContext = new RequestContext(httpContextBase, routeData);

return new UrlHelper(requestContext);
Excited answered 9/1, 2010 at 3:32 Comment(4)
It contains the URL of my site. There, I removed it.Excited
Considering that the UrlHelper class relies on the request context (and the HTTP context), constructing those context objects manually could yield unexpected results. If HttpContext.Current is null and you use this approach, I would proceed with caution.Chanteuse
Beware this answer - the dummied RequestContext results in a UrlHelper that always returns empty string.Literatim
Anyone know how to make this work in .NET Core?Talipes
V
284

Helpful tip, in any ASP.NET application, you can get a reference of the current HttpContext

HttpContext.Current

which is derived from System.Web. Therefore, the following will work anywhere in an ASP.NET MVC application:

UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
url.Action("ContactUs"); // Will output the proper link according to routing info

Example:

public class MyModel
{
    public int ID { get; private set; }
    public string Link
    {
        get
        {
            UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
            return url.Action("ViewAction", "MyModelController", new { id = this.ID });
        }
    }

    public MyModel(int id)
    {
        this.ID = id;
    }
}

Calling the Link property on a created MyModel object will return the valid Url to view the Model based on the routing in Global.asax

Vermiform answered 9/1, 2010 at 2:42 Comment(8)
Are you sure there's a HttpContext.Current.Request.RequestContext? HttpContext.Current.Request seems not to have a RequestContext.Excited
Thats odd. I just tested this solution out and it works perfectly. I'm running ASP.NET MVC 2 Preview 2, but I think this works across all versions. Not sure why it's not working for you. Are you creating the class outside of an MVC project? Also make sure there are using for both System.Web and System.Web.MvcVermiform
I'm on an ASP.NET MVC 1 project, I thought about missing usings but I have both of them.Excited
Not really sure why it's not showing. If anyone else could confirm this doesn't exist in ASP.NET MVC 1 that would be great. I only have one machine with VS2010 and MVC 2 installed. If you're interested, MVC RC 2 haacked.com/archive/2009/12/16/aspnetmvc-2-rc.aspxVermiform
Thanks Baddie. Worked like a champ. Using MVC 2 though.Quinte
Note that Request.RequestContex is supported in .NET4+Skilling
Doesn't work. UrlHelper.Action will always return null. See also #18151514Feliks
And if you need UrlHelper.GenerateContentUrl(String, HttpContextBase) you can call it like this: UrlHelper.GenerateContentUrl(contentPath, HttpContext.Current.Request.RequestContext.HttpContext)Confiteor
E
70

I like Omar's answer but that's not working for me. Just for the record this is the solution I'm using now:

var httpContext = HttpContext.Current;

if (httpContext == null) {
  var request = new HttpRequest("/", "http://example.com", "");
  var response = new HttpResponse(new StringWriter());
  httpContext = new HttpContext(request, response);
}

var httpContextBase = new HttpContextWrapper(httpContext);
var routeData = new RouteData();
var requestContext = new RequestContext(httpContextBase, routeData);

return new UrlHelper(requestContext);
Excited answered 9/1, 2010 at 3:32 Comment(4)
It contains the URL of my site. There, I removed it.Excited
Considering that the UrlHelper class relies on the request context (and the HTTP context), constructing those context objects manually could yield unexpected results. If HttpContext.Current is null and you use this approach, I would proceed with caution.Chanteuse
Beware this answer - the dummied RequestContext results in a UrlHelper that always returns empty string.Literatim
Anyone know how to make this work in .NET Core?Talipes
C
51

A UrlHelper can be constructed from within a Controller action with the following:

 var url = new UrlHelper(this.ControllerContext.RequestContext);
 url.Action(...);

Outside of a controller, a UrlHelper can be constructed by creating a RequestContext from the RouteTable.Routes RouteData.

HttpContextWrapper httpContextWrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
UrlHelper urlHelper = new UrlHelper(new RequestContext(httpContextWrapper, RouteTable.Routes.GetRouteData(httpContextWrapper)));

(Based on Brian's answer, with a minor code correction added.)

Cherie answered 9/1, 2010 at 2:4 Comment(7)
But I don't have a controller in the model.Excited
Okay I apologize, I wasn't sure exactly where the code was being executed. Let me take a look...Cherie
No need to create a new request context: var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);Deuterogamy
Nice @bradlis7. Is that MVC 5?Cherie
It works in MVC 5, but @Vermiform also had it in his answer back in 2010, so it may have been in the newest version at that time.Deuterogamy
Oh, sure enough. Cool.Cherie
Doesn't work, because this.ControllerContext.RequestContext is a HttpRequestContext, and constructor of UrlHelper expects a RequestContext. Both classes are unrelated.Feliks
W
8

Yes, you can instantiate it. You can do something like:

var ctx = new HttpContextWrapper(HttpContext.Current);
UrlHelper helper = new UrlHelper(
   new RequestContext(ctx,
   RouteTable.Routes.GetRouteData(ctx));

RouteTable.Routes is a static property, so you should be OK there; to get a HttpContextBase reference, HttpContextWrapper takes a reference to HttpContext, and HttpContext delivers that.

Woodpile answered 9/1, 2010 at 2:14 Comment(1)
This will not work, though it's very close. See my answer below.Cherie
F
4

After trying all the other answers, I ended up with

$"/api/Things/Action/{id}"

Haters gonna hate ¯\_(ツ)_/¯

Feliks answered 9/3, 2018 at 14:58 Comment(0)
M
0

I was trying to do something similar from within a page (outside of a controller).

UrlHelper did not allow me to construct it as easily as Pablos answer, but then I remembered a old trick to effective do the same thing:

string ResolveUrl(string pathWithTilde)
Magnus answered 17/2, 2015 at 15:39 Comment(0)
M
0

Previous responses didn't help me. My approach has been create an action in my Home controller with the same functionality that UrlHelper.

    [HttpPost]
    [Route("format-url")]
    public ActionResult FormatUrl()
    {
        string action = null;
        string controller = null;
        string protocol = null;
        dynamic parameters = null;

        foreach (var key in this.Request.Form.AllKeys)
        {
            var value = this.Request.Form[key];
            if (key.Similar("action"))
            {
                action = value;
            }
            else if (key.Similar("controller"))
            {
                controller = value;
            }
            else if (key.Similar("protocol"))
            {
                protocol = value;
            }
            else if (key.Similar("parameters"))
            {
                JObject jObject = JsonConvert.DeserializeObject<dynamic>(value);

                var dict = new Dictionary<string, object>();
                foreach (var item in jObject)
                {
                    dict[item.Key] = item.Value;
                }

                parameters = AnonymousType.FromDictToAnonymousObj(dict);
            }
        }

        if (string.IsNullOrEmpty(action))
        {
            return new ContentResult { Content = string.Empty };
        }

        int flag = 1;
        if (!string.IsNullOrEmpty(controller))
        {
            flag |= 2;
        }

        if (!string.IsNullOrEmpty(protocol))
        {
            flag |= 4;
        }

        if (parameters != null)
        {
            flag |= 8;
        }

        var url = string.Empty;
        switch (flag)
        {
            case 1: url = this.Url.Action(action); break;
            case 3: url = this.Url.Action(action, controller); break;
            case 7: url = this.Url.Action(action, controller, protocol); break;
            case 9: url = this.Url.Action(action, parameters); break;
            case 11: url = this.Url.Action(action, controller, parameters); break;
            case 15: url = this.Url.Action(action, controller, parameters, protocol); break;
        }

        return new ContentResult { Content = url };
    }

Been an action, you can request it from anywhere, even inside the Hub:

            var postData = "action=your-action&controller=your-controller";

            // Add, for example, an id parameter of type integer
            var json = "{\"id\":3}";
            postData += $"&parameters={json}";

            var data = Encoding.ASCII.GetBytes(postData);

#if DEBUG
            var url = $"https://localhost:44301/format-url";
#else
            var url = $"https://your-domain-name/format-url";
#endif

            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "application/text/plain";
            request.ContentLength = data.Length;

            using (var stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }

            var response = (HttpWebResponse)request.GetResponse();
            var link = new StreamReader(stream: response.GetResponseStream()).ReadToEnd();

You can get source code of AnonymousType here.

Moulden answered 13/3, 2022 at 11:47 Comment(0)
B
-30

I think what you're looking for is this:

Url.Action("ActionName", "ControllerName");
Brenn answered 9/1, 2010 at 2:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.