Irfan Y
Irfan Y

Reputation: 1310

Request.Url.Scheme not working as expected

I am using asp.net MVC. My code is:

var url = Url.Action(
                   "ConfirmEmail", "Account",
                   new { userId = id, code = code },
                   protocol: Request.Url.Scheme);

On localhost it is returning right url i.e,

https://localhost:44300/Account/ConfirmEmail?userId=bed34d05-2b7b-49b8-940e-0d89d4870d17&code=AQAA

But on live website its not working as expected. The url it returns is:

http://newtemp.apphb.com:16169/Account/ConfirmEmail?userId=7ba65316-0901-4c27-9371-588a5e945baa&code=AQAA

the portion :16169 is addational. Where it came from and how to remove this?

My detailed code is:

public async Task<bool> SendMailtoConfirmEmailAddress(string id,string name,string email)
        {
            var user = await db.AspNetUsers.FindAsync(id);
            if (user.EmailConfirmed)
            {
                return false;
            }
            try
            {
                var provider = new DpapiDataProtectionProvider("http://newtemp.apphb.com/");
                UserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, string>(provider.Create("UserToken"))
                    as IUserTokenProvider<ApplicationUser, string>;

                var code = await UserManager.GenerateEmailConfirmationTokenAsync(id);
                var callbackUrl = Url.Action(
                   "ConfirmEmail", "Account",
                   new { userId = id, code = code },
                   protocol: Request.Url.Scheme);

                ElectronicsController.sendEmail(email, "Welcome to dealkar.pk - Confirm Email address", "Hello " + name + "!<br/>Confirm your email address by clicking <a href=\"" + callbackUrl + "\">here</a> OR " + callbackUrl);

            }
            catch (Exception e)
            {
                string s = e.ToString();
            }
            return true;
        }

Upvotes: 4

Views: 4954

Answers (1)

NightOwl888
NightOwl888

Reputation: 56879

Where it came from?

The port is resolved from the current request. Unfortunately, Microsoft didn't account for when a URL is generated on a server that is running a non-default port and it will just use the current one (whether or not the protocol is the same as the current request).

how to remove this?

You can create an extension method to tell the UrlHelper to use the default port regardless of the port of the incoming request.

This example creates a fake HTTP Context based on the default port (80 for HTTP, 443 for HTTPS), then uses a new instance of UrlHelper to resolve the URL.

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

public static class UrlHelperExtensions
{
    public static string Action(this UrlHelper helper, string actionName, string controllerName, object routeValues, string protocol, bool defaultPort)
    {
        return Action(helper, actionName, controllerName, routeValues, protocol, null, defaultPort);
    }

    public static string Action(this UrlHelper helper, string actionName, string controllerName, object routeValues, string protocol, string hostName, bool defaultPort)
    {
        if (!defaultPort)
        {
            return helper.Action(actionName, controllerName, new RouteValueDictionary(routeValues), protocol, hostName);
        }

        string port = "80";
        if (protocol.Equals("https", StringComparison.OrdinalIgnoreCase))
        {
            port = "443";
        }

        Uri requestUrl = helper.RequestContext.HttpContext.Request.Url;
        string defaultPortRequestUrl = Regex.Replace(requestUrl.ToString(), @"(?<=:)\d+?(?=/)", port);
        Uri url = new Uri(new Uri(defaultPortRequestUrl, UriKind.Absolute), requestUrl.PathAndQuery);

        var requestContext = GetRequestContext(url);
        var urlHelper = new UrlHelper(requestContext, helper.RouteCollection);

        var values = new RouteValueDictionary(routeValues);
        values.Add("controller", controllerName);
        values.Add("action", actionName);

        return urlHelper.RouteUrl(null, values, protocol, hostName);
    }

    private static RequestContext GetRequestContext(Uri uri)
    {
        // Create a TextWriter with null stream as a backing stream 
        // which doesn't consume resources
        using (var writer = new StreamWriter(Stream.Null))
        {
            var request = new HttpRequest(
                filename: string.Empty,
                url: uri.ToString(),
                queryString: string.IsNullOrEmpty(uri.Query) ? string.Empty : uri.Query.Substring(1));
            var response = new HttpResponse(writer);
            var httpContext = new HttpContext(request, response);
            var httpContextBase = new HttpContextWrapper(httpContext);
            return new RequestContext(httpContextBase, new RouteData());
        }
    }
}

Usage

        var url = Url.Action("ConfirmEmail", "Account",
               new { userId = 123, code = 456 },
               protocol: Request.Url.Scheme,
               defaultPort: true);

Upvotes: 1

Related Questions