Thursday, December 27, 2012

IIS - Cannot read configuration file due to insufficient permissions

Did you ever try to create a new website in IIS, run it and get this error?


The requested page cannot be accessed because the related configuration data for the page is invalid.

Easy fix!

  • Go to IIS, right click on your website and choose Edit Permissions. 
  • Click on the Security tab.  
  • Click on the Edit button.  
  • Click on the Add button.  
  • On the Enter the object names to Select, Type "Everybody" (without the quotes)
  • Click all the OK buttons.

Had to list this down because i keep forgetting!

Tuesday, December 11, 2012

Combining ASP.NET Webforms and MVC into one solution


If you've been wanting a Hybrid ASP.NET Application with both Webforms and MVC in it, then you've come to the right place.  You may have an existing Webforms site and need to migrate to MVC but you don't know where to begin.  There are different approaches to do this.  Of course creating an MVC web application from scratch is the cleanest and easiest to maintain.  However you might need to deploy 2 websites in parallel to do this transition.  If you are not able to deploy 2 websites in parallel then an option is to add MVC code into your Webforms project .  This will definitely work.  The only thing is that you lose some nice MVC development features like Scaffolding. But if you're ok with creating these MVC files manually then it won't be an issue.  Thanks to Scott Hanselman's Blog for most of the information.  I am using Visual Studio 2012 for this test.

1.  First, add these references to your Webforms project:
a.     System.Web.MVC
b.     System.Web.Razor


2.  Add this in appSettings to your web.config

<appSettings>
   <add key="ClientValidationEnabled" value="true"/>
   <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>



3.  Add these under compilation to your web.config

<   <compilation debug="true" targetFramework="4.0">
  <assemblies>
    <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add assembly="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  </assemblies>
</compilation>


4.  Add these namespaces in pages to your web.config

<system.web>
  <pages>
     <namespaces>
       <add namespace="System.Web.Helpers" />
       <add namespace="System.Web.Mvc" />
       <add namespace="System.Web.Mvc.Ajax" />
       <add namespace="System.Web.Mvc.Html" />
       <add namespace="System.Web.Routing" />
       <add namespace="System.Web.WebPages"/>
     </namespaces>
   </pages>
</system.web>



5.  Add this to your web.config. This is under the <configuration> node
<system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>



6.  Add this to your Global.asax

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
        filters.Add(new HandleErrorAttribute());
}

public static void RegisterRoutes(RouteCollection routes)
 {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );

 }

protected void Application_Start()
{
        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
}


7.  If you want the default Webforms page to load first, add this line before the other routes.  Or else the MVC default page will render first.

            routes.MapPageRoute("MyWebFormDefault", "", "~/Default.aspx");
        

     8.  Make sure you add these to your Global.asax

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

Copy some MVC files into your Webforms project.  In my case, I copied the Controllers, Models, and Views folders.  For simple testing purposes, I removed files related to AccountController and its views.  I only left the HomeController and its views plus the Shared views.

Thats it!  If you run your application, you should see the default.aspx on the browser.  If you type in a friendly URL such as http://localhost:8023/Home you should see the MVC Home page.


Saturday, January 7, 2012

Configuring an ASP.NET 4.0 Web Application

We often wonder why configuring ASP.NET web applications sometimes is a pain.  It's actually not, we just forget to do a little tweaks which is why I am writing this post:  so that I don't forget next time!

Ok. So here's the situation, I had to reinstall my Windows 7 Ultimate on my 64-bit machine.  All my web application code was in that box so that meant i had to configure my development environment from scratch!  So I Installed SQL Server 2008 R2 before Visual Studio 2010 Ultimate.  Yes, that is correct, SQL Server first. I've had experiences when installing VS first would install its own SQL Server Express edition, then installing SQL Server 2008 R2 would use that SQL Server Express instance.  I could have missed something during the installation but I always find that installing SQL Server before VS is nuch smoother.

So now i installed IIS 7.5 by going to Control Panel -> Programs -> Turn Windows Feature On or Off and check Internet Information Services.  Remember to check the sub features under these if you need any of them.  I had to check ASP.NET (of course!) under Application Development Features and URL Authorization under Security.

Then I add a Web Application in IIS.  You could add a Web Site with a different port number (such as 8080, 8081, etc) or you could just add  a Web Application under the Default Web Site which is port 80 by default.  Then you add a new Application Pool because the default application pool uses .NET 2.0.  So make sure your new Application Pool (which you can call anything you want) must be using .NET 4.0.  Also make sure that you go to Advanced Settings and select the correct Application Pool Identity.  In my case, I chose Network Service because that's the same account that is accessing my SQL Server database.

After all this, you might find that your pages still won't run.  Here are some of the errors that you might see:
  
HTTP Error 500.19 - Internal Server Error with Error Code: 0x80070021 
The requested page cannot be accessed because the related configuration data for the page is invalid.
Config Error: This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault="Deny"), or set explicitly by a location tag with overrideMode="Deny" or the legacy allowOverride="false". 

 To get around this error, you will have to open this file

 Windows\System32\inetsrv\config\applicationHost.config

Change both these entries from Deny to Allow:

<section name="handlers" overrideModeDefault="Allow" />
<section name="modules" allowDefinition="MachineToApplication" overrideModeDefault="Allow" />

HTTP Error 404.3 - Not Found

The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.
 

HTTP Error 500.21 - Internal Server Error
Handler "PageHandlerFactory-Integrated" has a bad module "ManagedPipelineHandler" in its module list

These errors appear because possibly you have not installed ASP.NET and registered it with IIS.  So you should follow these steps.

If you have not installed the ASP.NET feature, add it through the Control Panel -> Programs -> Turn Windows Feature On or Off and check Internet Information Services I mentioned above.

Then register ASP.NET with IIS by doing the following.

Go to c:\Windows\Microsoft.NET\Framework\v4.0.30319 and run aspnet_regiis.exe -i

If you have a 64-bit machine, go to the c:\Windows\Microsoft.NET\Framework64\v4.0.30319 folder instead.

An error has occurred: 0x8007b799
You must have administrative rights on this machine in order to run this tool.
 


Turning off the UAC fixes this issue.

Start > Control Panel > System and Security > Action Center > Choose Your UAC Level - Set this to Never Notify (Requires a restart);

After doing all these little tweaks, my web application was running fine!

Good luck!

Tuesday, March 1, 2011

MVC Errors when first loading the page

You may run into permission problems when you create an MVC project and view the page for the first time.

Here are some errors you may get:


Cannot read configuration file due to insufficient permissions 

The solution to this is to make sure that the Application Pool is running under an account with permissions to your web root folder. Open Windows Explorer, right click on the directory where your web files are located.  Right-click and choose Properties.  Click on the Security tab and click Edit.  Add the Network Service user and give it Read access.

Open IIS.  Double click on Application Pools.  Select the Application Pool your website is using.  Right click on it and choose Advanced Settings.  Under Process Model change the Identity to Network Service.

You do not have permission to view this directory or page because of the access control list (ACL) configuration or encryption settings for this resource on the Web server.

The solution to this is to add the user IUSR to your web root folder and give it Read access.  Same procedure as the first step above.

Saturday, August 21, 2010

Dynamically removing parameters from an ObjectDataSource and LinqDataSource within a ReportViewer LocalReport

If you've been using the ReportViewer in your web application, you've probably encountered having to deal with dynamically adding and removing parameters from a datasource to dynamically change your query statement in your report.  This is useful if you provide your users a filter for ad hoc reporting.  You could have radio buttons or drop downs on your report page to allow your users to filter search results.

If you are using Linq in your application, there's a LinqDataSource control that you can use to make calls using your DataContext object.  In the example below, my LinqDataSource object has a Where condition parameter:  StudentStatusID == @StudentStatusID


 <asp:LinqDataSource ID="ldsExamCompleted" runat="server" 
        ContextTypeName="MyDataContext" EntityTypeName="" 
        Select="new (StudentID, FirstName, LastName, ExamDate)" 
        TableName="Students" 
        
    
        Where="StudentStatusID == @StudentStatusID" 
        OrderBy="LastName">
        <WhereParameters>
            <asp:QueryStringParameter Name="StudentStatusID" QueryStringField="status" 
                Type="Int16" />
        </WhereParameters>
    </asp:LinqDataSource>


 For example on the aspx page we have an html dropdown named Status with 3 possible values:  Active (value=1), Inactive (value=2) and Both (value=0).  Each time we change the value on the dropdown, we pass the value as a querystring parameter.  Let's say we choose Both on the dropdown.  Our code behind would look like this.

            if (!IsPostBack)
            {
                ReportViewer1.LocalReport.DataSources.Clear();

                ParameterCollection examParams = ldsExamCompleted.WhereParameters;

                //if user chooses 'Both' on status dropdown
                if (Request.QueryString["status"] == "0"
                {

                    Parameter p = examParams["StudentStatusID"];
                    ldsExamCompleted.WhereParameters.Remove(p);
                }
            }

Here, we removed the where condition parameter "StudentStatus == @StudentStatusID" from our Linq select query and our DataContext object returns all results.  That simple.

So when should you use an ObjectDataSource in your ReportViewer?  An ObjectDataSource is ideal if you use  TableAdapters.  TableAdapters provide communication between your application and the database.  TableAdapters are automatically created when you add a DataSet to your project.  You pass parameters from your ObjectDataSource to your TableAdapter if the Select method on your TableAdapter expects parameters.   Below is a sample of an ObjectDataSource on the aspx page:

   <asp:ObjectDataSource ID="odsExamCompleted" runat="server" 
        OldValuesParameterFormatString="original_{0}" SelectMethod="GetDataByStatusID" 
        
        TypeName="dsStudentTableAdapters.vwExamResultsTableAdapter">
        <SelectParameters>
            <asp:QueryStringParameter Name="StudentStatusID" QueryStringField="status" 
                Type="Int16" />
        </SelectParameters>
    </asp:ObjectDataSource>



Unlike a LinqDataSource where you can remove Where Condition parameters such as "StudentStatusID == @StudentStatusID", an ObjectDataSource can only remove the Select parameter StudentStatusID.  So if you try to remove this parameter from your ObjectDataSource, it will error out since your TableAdapter expects this parameter on its where clause.  You will need to add another Select method to your TableAdapter if you need to have a parameterless query or one with additional parameters.  So on our TableAdapter, I defined 2 Select methods.  One named GetDataByStatusID which expects a @StudentStatusID parameter and one named GetData which doesn't expect any parameter. 


Let's say we want to achieve the same results as our LinqDataSource  example above, this is what we should do on our code behind:
           
               if (!IsPostBack)
            {
                ReportViewer1.LocalReport.DataSources.Clear();

                ParameterCollection examCompletedParams = odsExamCompleted.SelectParameters;

                //if user chooses 'Both' on status dropdown
                if (Request.QueryString["status"] == "0"
                {

                    Parameter p = examCompletedParams["StudentStatusID"];
                    odsExamCompleted.SelectParameters.Remove(p);

                    odsExamCompleted.SelectMethod = "GetData";
                }
            }

Hope this helps anyone implementing ad hoc reporting on their web applications.


Friday, August 20, 2010

How to secure a web folder in IIS 7 by denying IP addresses other than your own

I spent 3 days trying to think of a way to secure a web folder which contained sensitive files. I wanted public users to be able to upload files to that folder but i didn't want them to be able to view it. In other words, Write but no Read. The only way to view the file is within my application which the user needs to be authenticated using Forms Authentication (SqlMembershipProvider). Now there were different approaches on how to secure web folders. The 1st one is using ASP.NET authorization which looks like this on a web.config:

<location path="uploads">
     <system.web>
          <authorization>
               <allow users="myself"/>
               <deny users="*"></deny>
         </authorization>
     </system.web>
</location>

Sadly, this didn't work. Anyone could just type in the full url path to the file and view it without being authenticated.

The 2nd one is through IIS 7 URL Authorization which looks like this on a web.config:

<location path="uploads">
       <system.webServer>
             <security>
                  <authorization>
                          <add accessType="Allow" users="myself" />
                          <remove users="*" roles="" verbs="" />
                  </authorization>
             </security>
       </system.webServer>
</location>

Excitingly, this blocked all requests to the files I wanted to hide. Sadly, even my application wasn't able to access the files. IIS 7 URL Authorization doesn't care about any ASP.NET authenticated user.

The 3rd option was the one that worked for me. Using IIS 7 Url Rewrite, I created a rule denying requests from IP addresses that don't match my server's IP address. Just put a web.config file into the folder you want to secure and put the entry below. Just replace the IP address below with your server's IP.

<system.webServer>
      <rewrite>
            <rules>
                 <rule name="RequestBlockingRule1" patternSyntax="Wildcard"                   stopProcessing="true">
                        <match url="*" />
                                <conditions>
                                        <add input="{REMOTE_ADDR}" pattern="77.11.36.12" negate="true" />
                                </conditions>
                                <action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." statusDescription="You do not have permission to view this directory or page using the credentials that you supplied." />
                  </rule>
           </rules>
      </rewrite>
</system.webServer>

Hope this helps someone out there.

Monday, July 19, 2010

WebHost4Life too good to be true

After hosting my sites with GoDaddy for years, i finally got fed up. GoDaddy won't allow full trust .NET hosting. I wanted to run Microsoft Reports on my site but all i got was this error:

Security Exception

Description: The application attempted to perform an operation not allowed by the security policy. To grantjavascript:void(0) this application the required permission please contact your system administrator or change the application's trust level in the configuration file.

Exception Details: System.Security.SecurityException: That assembly does not allow partially trusted callers.

I tried searching on the web for hosting companies that allowed full trust .NET and there were 2 that I saw: WebHost4Life and DiscountASP.NET. WebHost4Life was cheaper and had a unlimited storage shared hosting plan. They also had a lot of good reviews about customer service. I wanted to do a test drive on WebHost4Life so I tried signing up for the basic plan which cost $4.95 per month. I went to the Control Panel looking for the MS-SQL admin but then realized that the basic plan didn't include MS-SQL. So I had to upgrade to the next plan which is Advanced which cost $9.95 per month and gives you 5 MS-SQL databases.

The first problem I encountered was not being able to use the Database Publishing Wizard to export my data into WebHost4Life. With GoDaddy this worked like a charm. I called WebHost4Life support asking them if they had a Service Address I could connect to such as (https://mydatabase.webhost4life.com) but I guess they didn't know what I was talking about. They even asked me to try weird addresses like https://g3misa.webhost4life.mysql.com! How weird is that!!! They transferred me to a Senior Support Analyst who even said that connecting remotely to WebHost4Life databases is not allowed. Hello!?? It clearly says on your website that it is strongly suggested to use SQL Management Studio to connect remotely to your database. Anyway, I gave up on that option. So now my only option for exporting my data is running insert scripts on their server. Since WebHost4Life recommends using SQL Management Studio to connect to their databases remotely, this allows you to create and edit database objects directly from your SQL Management Studio client. While trying to do so, I kept getting a credentials error. So I tried creating several SQL accounts and still got the same credentials error. I contacted their Tech Support once again and they said they could reproduce the issue. It took them 1 day to solve this issue.

So after being able to run scripts on my WebHost4Life database, it was time for me to upload my .NET project files. Since they didn't have their own FTP client (which GoDaddy has), I downloaded FileZilla FTP client. I tried using the default ftp login credentials but it wouldn't connect. It kept giving me a Server Error. Even after creating several ftp accounts, the error wouldn't go away. So I had to call WebHost4Life for the 3rd time asking about this error. They said they would fix it asap. It took another day for me to be able to start using FTP.

Now that my files were on the server, I was so excited to see my pages. So after entering the URL, I get a 500-Internal Server Error. So I go to my WebHost4Life Control Panel and clicked on IIS Administration Console. There i noticed that the .NET Runtime is defaulted to .NET 1.1. So I changed it to .NET 3.5. Also i had to create a new folder below my root folder which had to be set as the application folder. They said it would take 2 hours at the most for these changes to take effect. After waiting more than 2 hours, i still got the same Internal Server Error. So I called WebHost4Life for the 4th time asking about this problem. They is what they said:

We are experiencing the issue with our server and we are aware of the issue. Our engineers are working on the issue and it will be resolved as soon as possible. Once the issue is resolved, you will be able to access your website.

Out of curiousity I asked if I was the only one getting this error and they said No it wasn't only me. That suddenly gave me the chills. How can a hosting company provide this kind of service??? Anyway, it's now my 3rd day with WebHost4Life and I haven't seen how my website looks except for the 500-Internal Server Error. I'm giving this another day. If they can't fix it i am moving to DiscountASP.net.

Ok, the Internal Server Error is gone after waiting for a day. The database is working fine. Problem is that whenever i create a new database login, it can't connect to the database at all. That's it, i'm out of here. At this very moment, I am actually trying out DiscountAsp.NET hosting. What took me 4 days to set up in WebHost4Life, took me only an hour on DiscountAsp.NET. What a breeze!!! For all you ASP.NET and MS-SQL Developers, if you need fast, reliable, affordable, great customer service, DiscountAsp.NET is the way to go!!!