I uploaded nopcommerce solution to appharbor (using this method Can't build notcommerce project under appharbor) and solution succesfully builded, but I receiving 403 error - Forbidden: Access is denied when trying to open page(Allow write-access to file system is set to true).
Thanks and hope for your help
The problem is that the standard NopCommerce solution contains two Web Projects. AppHarbor only deploys one web project per application, and in this case, we happen to deploy Nop.Admin which is not what you want.
To resolve this, you should take advantage of the AppHarbor solution file convention and create an AppHarbor.sln solution file that only references the Nop.Web project.
We use a wrapper in our base controller to ensure that all of our code is oblivious to appharbor port changing.
First, fix in Webhelper.cs:75
public virtual string GetThisPageUrl(bool includeQueryString, bool useSsl)
{
string url = string.Empty;
if (_httpContext == null)
return url;
if (includeQueryString)
{
string storeHost = GetStoreHost(useSsl);
if (storeHost.EndsWith("/"))
storeHost = storeHost.Substring(0, storeHost.Length - 1);
url = storeHost + _httpContext.Request.RawUrl;
}
else
{
#if DEBUG
var uri = _httpContext.Request.Url;
#else
//Since appharbor changes port number due to multiple servers, we need to ensure port = 80 as in AppHarborRequesWrapper.cs
var uri = new UriBuilder
{
Scheme = _httpContext.Request.Url.Scheme,
Host = _httpContext.Request.Url.Host,
Port = 80,
Path = _httpContext.Request.Url.AbsolutePath,
Fragment = _httpContext.Request.Url.Fragment,
Query = _httpContext.Request.Url.Query.Replace("?", "")
}.Uri;
#endif
url = uri.GetLeftPart(UriPartial.Path);
}
url = url.ToLowerInvariant();
return url;
}
So what we did is simply add files from https://gist.github.com/1158264 into Nop.Core\AppHarbor
and modified base controllers:
nopcommerce\Presentation\Nop.Web\Controllers\BaseNopController.cs
public class BaseNopController : Controller
{
protected override void Initialize(RequestContext requestContext)
{
//Source: https://gist.github.com/1158264
base.Initialize(new RequestContext(new AppHarborHttpContextWrapper(System.Web.HttpContext.Current),
requestContext.RouteData));
}
//Same file from here downwards...
}
nopcommerce\Presentation\Nop.Web.Admin\Controllers\BaseNopController.cs
public class BaseNopController : Controller
{
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
//set work context to admin mode
EngineContext.Current.Resolve<IWorkContext>().IsAdmin = true;
//Source: https://gist.github.com/1158264
base.Initialize(new RequestContext(new AppHarborHttpContextWrapper(System.Web.HttpContext.Current), requestContext.RouteData));
//base.Initialize(requestContext);
}
//Same file from here downwards...
}
Enable the Directory Browsing feature in IIS Express
Note This method is for the web developers who experience the issue when they use IIS Express.
To do this, follow these steps:
Open a command prompt, and then go to the IIS Express folder on your computer. For example, go to the following folder in a command prompt:
C:\Program Files\IIS Express
Type the following command, and then press Enter:
appcmd set config /section:directoryBrowse /enabled:true
refrence :https://support.microsoft.com/en-us/kb/942062
Related
I have a Cefsharp application which has Html pages with Javascript. Js makes API calls which happens fine if I open cef application in windows but most of API calls are not happening when I am opening same cef application through testcompelete.
On debugging application I am getting CORS warning.
I am using CefCustomScheme which has root folder path, schemeName and host name specified. And provided same shemeName and host name in Address in wpf: chromiumwebrowse tag. If I hard-code root folder path in Address Source binding, its working fine even in testcomplete.
private static CefCustomScheme GetAlmanacScheme(IAppSettings appSettings)
{
try
{
var almanacFolder = appSettings.Settings["ALMANAC_WIDGET_PATH"];
if (string.IsNullOrWhiteSpace(almanacFolder))
{
almanacFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Thermo", "InstConnectAgent", "AlmanacWidget");
}
var factory = new FolderSchemeHandlerFactory(almanacFolder, "thermo", "almanac");
return new CefCustomScheme() { SchemeName = "thermo", SchemeHandlerFactory = factory, IsCorsEnabled = true };
}
catch
{
return null;
}
}
and Source is
private const string WidgetUrl = "thermo://almanac";
public string Source
{
get
{
return WidgetUrl;
}
}
I'm trying to load a file on a self-hosted Owin based server.
WebApp.Start<Startup>("http://localhost:3001/");
Here is how I map the path:
var path = HostingEnvironment.MapPath("~");
it always returns null!
On the other hand, if the website is hosted in IIS (or express) the value of path is right.
How can I can populate this value for the Self-Hosted owin?
As owin self-hosting doesn't use iis, instead of virtual path, the absolute path should be used.
var path = HostingEnvironment.MapPath("~");
if (path == null)
{
var uriPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
path = new Uri(uriPath).LocalPath;
}
When running under OWIN under dnx, I found that many of the suggested workarounds around reading the current app domain would return the .dnx runttime folder - not the web app location.
Here's what worked for me inside OWIN startup:
public void Configure(IApplicationBuilder app)
{
app.Use(async (ctx, next) =>
{
var hostingEnvironment = app.ApplicationServices.GetService<IHostingEnvironment>();
var realPath = hostingEnvironment.WebRootPath + ctx.Request.Path.Value;
// do something with the real file -path
await next();
});
I used the ASP.NET Web Application template to create a new "Single Page Application" with "Authentication: Individual User Accounts". It will run with the default settings without any problem.
If I don't deploy the application to the root folder of the web server the authentication fails. The culprit is in the app.viewmodel.js file where the following code can be found:
self.addViewModel = function (options) {
var viewItem = new options.factory(self, dataModel),
navigator;
// Add view to AppViewModel.Views enum (for example, app.Views.Home).
self.Views[options.name] = viewItem;
// Add binding member to AppViewModel (for example, app.home);
self[options.bindingMemberName] = ko.computed(function () {
if (!dataModel.getAccessToken()) {
// The following code looks for a fragment in the URL to get the access token which will be
// used to call the protected Web API resource
var fragment = common.getFragment();
if (fragment.access_token) {
// returning with access token, restore old hash, or at least hide token
window.location.hash = fragment.state || '';
dataModel.setAccessToken(fragment.access_token);
} else {
// no token - so bounce to Authorize endpoint in AccountController to sign in or register
window.location = "/Account/Authorize?client_id=web&response_type=token&state=" + encodeURIComponent(window.location.hash);
}
}
return self.Views[options.name];
});
The line where window.location = "/Account..." redirects the browser to an URL offset at the root directory. Unfortunately just hard coding this to the new folder instead (which I would like to avoid anyway) does not solve the problem entirely.
The redirect seems to work at first but behind the scenes in the AccountController.csfile
Authorize()is called which in turn calls AuthenticationManager.SignIn(identity) and somehere there is magic going on. There is a redirect to http://localhost/foo/Account/Login?ReturnUrl=... and we're back where we started.
I am probably missing the obvious. I'd appreciate any pointers.
It's very easy to replicate. Just create a new web app with the default settings and then go into project properties and change the "Project Url" to something like http://localhost:49725/foo which moves the app to a new folder called "foo".
I'm facing same problem, the change I did was:
In the file app.viewmodel.js I added a new parameter (returnUrl):
// no token - so bounce to Authorize endpoint in AccountController to sign in or register
window.location = "/Account/Authorize?client_id=web&response_type=token&state="
+ encodeURIComponent(window.location.hash)
+ "&returnUrl=" + encodeURIComponent(window.location);
In the method ValidateClientRedirectUri of class ApplicationOAuthProvider I read this parameter and set as the return url:
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri;
if (!string.IsNullOrEmpty(context.Request.Query["returnUrl"]))
{
expectedRootUri = new Uri(context.Request.Query["returnUrl"]);
}
else
{
expectedRootUri = new Uri(context.Request.Uri, "/");
}
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
else if (context.ClientId == "web")
{
var expectedUri = new Uri(context.Request.Query["returnUrl"]);
context.Validated(expectedUri.AbsoluteUri);
}
}
return Task.FromResult<object>(null);
}
The solution for this is to open the app.viewmodel.js and locate the line that reads
/Account/Authorize?client_id=web&response_type=token&state="
and change it to match the path of your subfolder
/YOUR_SITE/Account/Authorize?client_id=web&response_type=token&state="
We're dynamically loading assemblies at startup and adding them as a reference:
BuildManager.AddReferencedAssembly(assembly);
The application supports installing new plugins at runtime. Following an install/uninstall action we are restarting the web application. I've tried:
HostingEnvironment.InitiateShutdown();
and
System.Web.HttpRuntime.UnloadAppDomain();
However, the new version of a plugin is not loaded - I believe this is due to how ASP.NET is aggressively caching referenced assemblies - especially ASP.NET MVC controllers.
In production this shouldn't be a problem since the plugin assembly version would be incremented each time. However, in development this is more of an issue since we don't wish to change the version number every time we make a slight change to a plugin.
How can we force the clearing of temp asp.net files, either programatically or using a post build event?
One solution is to "touch" global.asax but this seems a bit hacky to me.
I've used following piece of code to reset the application pool on demand. (Just connect this to a Controller Action).
Note : Since it's the application pool, you might want to check the impact to any other apps running on the same app pool.
public class IisManager
{
public static string GetCurrentApplicationPoolId()
{
// Application is not hosted on IIS
if (!AppDomain.CurrentDomain.FriendlyName.StartsWith("/LM/"))
return string.Empty;
// Application hosted on IIS that doesn't support App Pools, like 5.1
else if (!DirectoryEntry.Exists("IIS://Localhost/W3SVC/AppPools"))
return string.Empty;
string virtualDirPath = AppDomain.CurrentDomain.FriendlyName;
virtualDirPath = virtualDirPath.Substring(4);
int index = virtualDirPath.Length + 1;
index = virtualDirPath.LastIndexOf("-", index - 1, index - 1);
index = virtualDirPath.LastIndexOf("-", index - 1, index - 1);
virtualDirPath = "IIS://localhost/" + virtualDirPath.Remove(index);
var virtualDirEntry = new DirectoryEntry(virtualDirPath);
return virtualDirEntry.Properties["AppPoolId"].Value.ToString();
}
public static void RecycleApplicationPool(string appPoolId)
{
string appPoolPath = "IIS://localhost/W3SVC/AppPools/" + appPoolId;
var appPoolEntry = new DirectoryEntry(appPoolPath);
appPoolEntry.Invoke("Recycle");
}
public static void RecycleApplicationPool(string appPoolId, string username, string password)
{
string appPoolPath = "IIS://localhost/W3SVC/AppPools/" + appPoolId;
var appPoolEntry = new DirectoryEntry(appPoolPath, username, password);
appPoolEntry.Invoke("Recycle");
}
}
The overridden method is to cater for instances where you want to explicitly pass a user with Admin rights on the machine/server which hosts IIS instance.
And the controller action could be something like;
public string ResetAppPool()
{
var appPoolId = IisManager.GetCurrentApplicationPoolId();
if (appPoolId.Equals(string.Empty))
return "Application is not running inside an App Pool"; //May be not IIS 6 onwards
try
{
IisManager.RecycleApplicationPool(appPoolId); //Can only be used by Admin users
return string.Format("App pool {0} recycled successfully", appPoolId);
}
catch (Exception ex)
{
Logger.Error("Failed to recycle app pool : " + ex.StackTrace);
return string.Format("App pool {0} recycle failed", appPoolId);
}
}
I am trying to deploy a nopCommerce application to AppHarbor.
When I start the page I run into a runtime redirect loop however. I added a bit of debug logging and the problem seems to be this part in Global.asax.cs -> EnsureDatabaseIsInstalled():
if (!webHelper.GetThisPageUrl(false).StartsWith(installUrl, StringComparison.InvariantCultureIgnoreCase))
{
this.Response.Redirect(installUrl);
}
StartsWith comparison is always false because
GetThisPageUrl returns
http://[name].apphb.com:14275/install
and installUrl (via GetStoreLocation) returns
http://[name].apphb.com/install
Has anyone been able to make nopCommerce work with AppHarbor at all?
It looks like you will need to modify nopCommerce to omit the port number. I took a quick look at the source and there seems to be two possible solutions:
1) Changing the boolean argument from false to true in the EnsureDatabaseIsInstalled method should cause the GetThisPageUrl method to pick a different branch that generates the URL without the port number.
2) Updating the else branch in the GetThisPageUrl method (of "WebHelper.cs") to ignore the port number.
It's easier to pick the first solution, but patching the issue at its core will be better so you don't run into similar issues.
In addition to #TroelsThomsen fix, we use a wrapper in our base controller to ensure that all of our code is oblivious to appharbor port changing.
First, #TroelsThomsen fix in Webhelper.cs:75
public virtual string GetThisPageUrl(bool includeQueryString, bool useSsl)
{
string url = string.Empty;
if (_httpContext == null)
return url;
if (includeQueryString)
{
string storeHost = GetStoreHost(useSsl);
if (storeHost.EndsWith("/"))
storeHost = storeHost.Substring(0, storeHost.Length - 1);
url = storeHost + _httpContext.Request.RawUrl;
}
else
{
#if DEBUG
var uri = _httpContext.Request.Url;
#else
//Since appharbor changes port number due to multiple servers, we need to ensure port = 80 as in AppHarborRequesWrapper.cs
var uri = new UriBuilder
{
Scheme = _httpContext.Request.Url.Scheme,
Host = _httpContext.Request.Url.Host,
Port = 80,
Path = _httpContext.Request.Url.AbsolutePath,
Fragment = _httpContext.Request.Url.Fragment,
Query = _httpContext.Request.Url.Query.Replace("?", "")
}.Uri;
#endif
url = uri.GetLeftPart(UriPartial.Path);
}
url = url.ToLowerInvariant();
return url;
}
So what we did is simply add files from https://gist.github.com/1158264 into Nop.Core\AppHarbor
and modified base controllers:
nopcommerce\Presentation\Nop.Web\Controllers\BaseNopController.cs
public class BaseNopController : Controller
{
protected override void Initialize(RequestContext requestContext)
{
//Source: https://gist.github.com/1158264
base.Initialize(new RequestContext(new AppHarborHttpContextWrapper(System.Web.HttpContext.Current),
requestContext.RouteData));
}
//Same file from here downwards...
}
nopcommerce\Presentation\Nop.Web.Admin\Controllers\BaseNopController.cs
public class BaseNopController : Controller
{
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
//set work context to admin mode
EngineContext.Current.Resolve<IWorkContext>().IsAdmin = true;
//Source: https://gist.github.com/1158264
base.Initialize(new RequestContext(new AppHarborHttpContextWrapper(System.Web.HttpContext.Current), requestContext.RouteData));
//base.Initialize(requestContext);
}
//Same file from here downwards...
}