Changing URLs while moving project from development server to live server - asp.net

This is relatively a noob question.
While developing a ASP.Net website, if I am referring to a resource with ResolveUrl() method, it works on either live server or dev server but not on both, based on the Url provided.
For example, if my code tries to pick a resource with below code, it works on live server but not on development server as JScript.js is not under http://localhost:xx/Assets but is under http://localhost:xx/ApplicationName/Assets.
<script src='<%# ResolveUrl("~/Assets/JScript.js")%>' type="text/javascript"></script>
In order to make it work on both servers, I have to keep changing the URL according to the server I am working on.
I have been suffering this annoying problem for sometime but kept ignoring it.
Is there a better way to do it?
Thanks!

As posted in my comments above, you want to use a <%= %> vs. <%# %> to render an inline expression. You only use the hash symbol for inline data binding.

I know this might not be the out-the-box way, but I use these to make sure URLS are corect within my applications without issues. with these available in my Page/View's base classes...
public static string ApplicationRootUrl()
{
string port = String.Empty;
if (HttpContext.Current.Request.ServerVariables["SERVER_PORT"] != null && HttpContext.Current.Request.ServerVariables["SERVER_PORT"].ToString() != "80" && HttpContext.Current.Request.ServerVariables["SERVER_PORT"].ToString() != "443")
{
port = String.Concat(":", HttpContext.Current.Request.ServerVariables["SERVER_PORT"].ToString());
}
string protocol = "http://";
if (HttpContext.Current.Request.ServerVariables["SERVER_PORT_SECURE"] != null && HttpContext.Current.Request.ServerVariables["SERVER_PORT_SECURE"] != "0")
{
protocol = "https://";
}
return String.Concat(protocol, String.Concat(HttpContext.Current.Request.Url.Host, port, HttpContext.Current.Request.ApplicationPath, '/').Replace(#"//", #"/").ToLower());
}
/// <summary>
/// Expands a virtual URL to an absolute URL using the current application root url
/// </summary>
/// <param name="url"></param>
public static string ExpandUrl(string url)
{
if (url.Trim().StartsWith("~"))
{
return String.Concat(ApplicationRootUrl(), url.Substring(1).Replace("//", "/"));
}
if (url.Trim().StartsWith("www", StringComparison.OrdinalIgnoreCase))
{
return String.Concat("http://", url);
}
return url;
}

Try making http://localhost:xx/ApplicationName a virtual app on the DEV machine. That way ~/Assets will be in the root of the app on both PROD and DEV
Basically, the "Assets" directory needs to be in the root of your App, so you need to make the parent of "Assets" the App root on both Dev and PROD.

Related

ASP.NET MVC, Is it possible to code a Response.AppendToLog in one place which will act on every Request?

I am using ASP.NET 4.7 and MVC5 with C# with IIS Express locally and published to Azure App Services.
I want to add something like:
Response.AppendToLog("XXXXX Original IP = 12.12.12.12 XXXXX");
Which adds an Original IP address to the request string in the "request" column in the web server log.
If I add this to a specific "get" Action this works fine. However I do not want to add this code to every Action. Is it possible to place it more centrally such that it gets executed on every "Get" / Request. This may be a simple question, but the answer alludes me at present
Thanks for any wisdom.
EDIT: Is this via Custom Action Filters?
if (filterContext.HttpContext.Request.HttpMethod=="GET")
{
Response.AppendToLog... //I know this will not work as Response not known.
}
You almost know the answer. Try handling OnActionExecuted that gets you the Response.
public class CustomActionFilter : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Request.Method == HttpMethods.Get)
{
}
}
void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
var response = context.HttpContext.Response;
}
}
My solution to write out text:
filterContext.RequestContext.HttpContext.Response.AppendToLog("OrigIP");

MVC Permanent way to use redirects for HTTP to HTTPS and turn off for Dev Environment

I got this code from here.
Notice I remmed out the part that redirects to ISSExpress 44300 port because I want to use II7.5 on dev box without https.
public class CustomRequireHttpsFilter : RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
// The base only redirects GET, but we added HEAD as well. This avoids exceptions for bots crawling using HEAD.
// The other requests will throw an exception to ensure the correct verbs are used.
// We fall back to the base method as the mvc exceptions are marked as internal.
if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)
&& !String.Equals(filterContext.HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase))
{
base.HandleNonHttpsRequest(filterContext);
}
// Redirect to HTTPS version of page
// We updated this to redirect using 301 (permanent) instead of 302 (temporary).
string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
//if (string.Equals(filterContext.HttpContext.Request.Url.Host, "localhost", StringComparison.OrdinalIgnoreCase))
// {
// // For localhost requests, default to IISExpress https default port (44300)
// url = "https://" + filterContext.HttpContext.Request.Url.Host + ":44300" + filterContext.HttpContext.Request.RawUrl;
// }
filterContext.Result = new RedirectResult(url, true);
}
}
Then, in my FilterDonfig.cs I added this. What it does is it only uses the override above if Web.config has "Debug=false", which is what it has in Production. I don't need to run Release in my development environment, and I also don't want configure local IIS to handle SSL. Notice I remmed out the "RequireHttpsAttribute()" and used the new one above.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
if (!HttpContext.Current.IsDebuggingEnabled)
{
/////filters.Add(new RequireHttpsAttribute());
filters.Add(new CustomRequireHttpsFilter());
}
}
}
Am I doing the right thing? Is this how to make sure SEO is optimized because search bots only keep track of one website? My understanding is that "http" and "https" are considered 2 separate websites by search engines. Am I doing this in the right place? Not sure what other code I am getting in the way of.
===============
I asked my ISP about how to do permanent redirects and suggested this solution and they said:
Dear Customer,
We did not setup redirection. However, we corrected https bind setting in IIS to fix the problem.
I wonder if IIS can do the same thing and that is what they did. I hope I'm in the right forum :)
How about doing this at an IIS level using the URL rewrite module: http://forums.iis.net/t/1153050.aspx?URL+Rewrite+for+SSL+redirection
To turn it off in dev, just set the enabled rule to false in your dev web.config, but enable it for all servers/environments that have HTTPS set up.
I've used it in the past and its worked really well. Saves cluttering your app with code that isn't app related.

Running Angular and Web API in Visual Studio with IIS Express

I have a Visual Studio 2013 solution with a Web API project and a Web UI project (using Angular). I am using IIS Express.
Is there a way to set these projects up so that the Angular code can call the Web API project without hard-coding in the localhost and port number?
return $http.get("http://localhost:1561/api/products")
.then(function (response) {
return response.data;
});
If I hard-code localhost:1561 instead of just using the "/api/products" style I have to manually change the code before deploying to production and change it back to run it during development.
Is there an easier way?
Thanks!
var rootPath;
if (location.hostname === 'localhost') {
rootPath = 'http://localhost:1561';
} else {
rootPath = 'http://' + location.hostname;
}
return $http.get(rootPath + '/api/products')
.then(function (response) {
return response.data;
});
I actually wrote a simple utility method that returns the absolute URL so in your code you just have to type the relative URL. Below is this method which you should be able to call from your JS passing the relative URL (i.e. /api/products) or by converting it into a helper extension method...
// Code
public static string ToAbsoluteUrl(string relativeUrl)
{
if (string.IsNullOrEmpty(relativeUrl))
return relativeUrl;
if (HttpContext.Current == null)
return relativeUrl;
if (relativeUrl.StartsWith("/"))
relativeUrl = relativeUrl.Insert(0, "~");
if (!relativeUrl.StartsWith("~/"))
relativeUrl = relativeUrl.Insert(0, "~/");
var url = HttpContext.Current.Request.Url;
var port = url.Port != 80 ? (":" + url.Port) : String.Empty;
return String.Format("{0}://{1}{2}{3}",
url.Scheme, url.Host, port, VirtualPathUtility.ToAbsolute(relativeUrl));
}
From what I understand, it is not possible to do what I was attempting to do. Here are the options:
1) Use IIS on the local machine. That way you can set up the paths/virtual directories as necessary. This also makes it easier to call the API from other browsers during debugging. Note: This does require that you then run Visual Studio in admin mode from this point forward.
2) Put the two projects into one. For me this was not a valid option because I am need to deliver the UI code completely separate from the API code.
Hope this helps others trying to work with Angular and Web API.

subdomain CORS in webApi 2

I am using WebApi like I've learnt from http://t.co/mt9wIL8gLA
It all works well if I know exactly the perfect origin URI of my client's requests.
Too bad I am writing an enterprise-wide API, so my request to, say
http://apps.contoso.com/myApp/api/foobar
may come from apps all over my domain, say:
http://apps.contoso.com/CRMApp
http://apps.contoso.com/XYZ
http://www.contoso.com/LegacyApp
http://test.contoso.com/newApps/WowApp
...
and all the new apps my enterprise builds.
What is the best way to approach this? using Origins="*" is cheesy, adding origins to my WS source and redeploy is cheesier.
My current solution is writing a custom CorsPolicyAttribute like in http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api#cors-policy-providers
and read the allowed origins from appsettings in web.config.
A LITTLE better could be, inside the custom attribute, checking if the request Origin: header is from contoso.com, maybe with a regexp, and add it to allowed origins.
I am wondering if there is a better, more standard, way.
Use a DynamicPolicyProviderFactory. That's what I use...I even posted a question about it the other day that kind of shows how to add the allowed domains to the web.config file.
I ended up just writing an AuthorizationFilterAttribute, although I might have just done a regular FilterAttribute.
public class FilterReferals : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var request = actionContext.Request;
if (!AllowedReferers.GetAllowedReferersList().Contains(request.Headers.Referrer?.Host.ToLower()))
{
Challenge(actionContext);
return;
}
base.OnAuthorization(actionContext);
}
void Challenge(HttpActionContext actionContext)
{
var host = actionContext.Request.RequestUri.DnsSafeHost;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));
}
}

Where does ASP.NET virtual path resolve the tilde "~"?

Where does ASP.NET virtual path resolve the tilde ~ in the links, for example
<link rel="stylesheet" type="text/css" href="~/Css/Site.css" />
Does it redirect, or RedirectToAction in ASP.NET MVC?
It gets it from here:
VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
Here is the reflector output for PathHelpers class in System.Web.Mvc DLL:
private static string GenerateClientUrlInternal(HttpContextBase httpContext, string contentPath)
{
if (string.IsNullOrEmpty(contentPath))
{
return contentPath;
}
if (contentPath[0] == '~')
{
string virtualPath = VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
string str2 = httpContext.Response.ApplyAppPathModifier(virtualPath);
return GenerateClientUrlInternal(httpContext, str2);
}
NameValueCollection serverVariables = httpContext.Request.ServerVariables;
if ((serverVariables == null) || (serverVariables["HTTP_X_ORIGINAL_URL"] == null))
{
return contentPath;
}
string relativePath = MakeRelative(httpContext.Request.Path, contentPath);
return MakeAbsolute(httpContext.Request.RawUrl, relativePath);
}
See MSDN:Web Project Paths
ASP.NET includes the Web application
root operator (~), which you can use
when specifying a path in server
controls. ASP.NET resolves the ~
operator to the root of the current
application. You can use the ~
operator in conjunction with folders
to specify a path that is based on the
current root.
Basically, the purpose of the tilde is so that you can have a path that resolves properly even if you deploy your website to different places. Relative paths cannot accomplish this easily because controls may be rendered in different folders within your website. Absolute paths cannot accomplish this because your website may be deployed to different locations -- if nothing else, this is the case for test deployments made locally vs release deployments to the live server.
Server.MapPath can be used for similar reasons.
ASP.Net translates the tilde(~) with the application's root directory in every runat=server control. It is the equivalent for the HttpRuntime.AppDomainAppVirtualPath Property.

Resources