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!!!

Wednesday, April 28, 2010

Song Guru - A Facebook Game Application for Music Listeners

On my previous post, i mentioned about the first Facebook app i created called Song Guru.  The app is here:  http://apps.facebook.com/songguru

Here's the description:

Are you an avid music listener who knows the top songs in the land? Are you able to identify the artist and title when you hear a tune? The rules are simple, choose the correct title and artist of each song that we play for you. If you get all 10 correctly in the least amount of time, then you are a SONG GURU!

Playing the game is easy.  Here are the instructions:

When you hear the music play, find the correct song title on one of the boxes. Depending on the level you choose, there can be 10, 15, 20, 25 or 30 boxes (choices). When you click on the correct box, you earn a point, the box turns green and the next song plays. If you choose an incorrect box, you don't get a point (of course), and the box with the correct title turns red and the next song plays. Try it out now! Choose a Level and click Start to begin playing. Good luck!

Here's a screenshot of the game once it ends:

Tuesday, April 27, 2010

Building my first Facebook Application

So I went through the Facebook documentation on how to build an app. It was very easy and straightforward. There was an example app called Footprints. I downloaded the PHP code along with the Facebook PHP client library and FTP'd it to my host. I thought of using the example code as my initial code and i'd just build on top of it. I had to also set up a MySQL database on my host for the app to run. You just run an SQL script provided in the Footprint sample app.

Configuring the app on facebook was a breeze. You go to: http://www.facebook.com/developers/ and click on Set up New Application.  Facebook gives you an Application key and a secret key to use on your client code.

You just need to specify the Canvas url which is the absolute path to your index.php on your host. For example:

Canvas Callback Url : http://myhost.com/myapp/index.php

Of course you need to supply the Canvas Page Url. This is like searching for a domain name. I wanted to get apps.facebook.com/MusicGuru but it was already taken so I just chose apps.facebook.com/SongGuru

Then i went back to the Footprints PHP code and entered the App key and Secret key on the config.php.

So after setting this up, i was ready to test the Footprints app.

I went to apps.facebook.com/songguru to see what it looked like.

The first problem I ran into was a minor database config error.

Warning: mysql_query(): supplied argument is not a valid MySQL-Link resource in /.../footprints/lib.php on line 11

So, the fix was just to specify 'localhost' as the IP address of your database on the config.php file

// The IP address of your database
$db_ip = 'localhost';

Another error was when I clicked on Step button:

Warning: Missing argument 3 for FacebookRestClient::notifications_send(), called in /.../footprints/lib.php

The fix was to add a 3rd parameter to this API call:

$result = & $facebook->api_client->notifications_send($to, ' stepped on you. ' .'../apps.facebook.com/songguru/.', 'app_to_user');

Another error was this:

Fatal error: Call to undefined method FacebookRestClient::feed_publishActionOfUser() in /.../footprints/lib.php on line 58

Turns out that this API call is obsolete:

$facebook->api_client->feed_publishActionOfUser($feed_title, $feed_body);


So I replaced it with this:

$facebook->api_client->feed_publishUserAction($feed_title, $feed_body);

After that most of it seemed to work. I could see stuff saved to the database.

Now it was time to create the functionality for SongGuru. So i got rid of some example code and retained the important ones for logging in, displaying the name, and publishing the stream. Next thing to do was create the client side stuff. When i configured my App earlier, I had been deciding whether to use an iframe or FBML for my Canvas render method. I chose the FBML of course. The iframe approach was too easy. I wanted to burden myself with Facebook code.

So with FBML, you don't need to put 'html' and 'body' tags. This is already taken care of by the canvas. You can put html tags, css, javascript and FBML (Facebook's tag-based API calls). And so i put all those.

When I tested the app once again I was wondering why my Javascript wasn't working??? If you choose FBML as your render method, most of your javascript functions will not work. You have to use FBJS which is Facebook's own Javascript wrapper functions. For example:

myAnchor.href = 'blogger.com' becomes myAnchor.setHref('.blogger.com')
myButton.value = 'Submit' becomes myButton.setValue('Submit')
myDiv.innerHTML = '' becomes myDiv.setTextValue('')

Setting css classes would have to be like this:

document.getElementById('panel').setStyle('visibility', 'visible');

Facebook canvas already has the clearfix css class defined so you do not have to write your own clearfix rule. Just add class='clearfix' to your tags that need it.

Also getElementsByTagName and getElementsByName do not work. You can only use getElementsById.

You can also make Facebook Javascript API calls inside the canvas. For example:

Facebook.showPermissionDialog('publish_stream,read_stream');

This will display a dialog box asking the user's permission for automatic updates by the app.

To sum it up, it was very easy creating a Facebook app. I love how Facebook created their APIs. Of course you need to think of something interesting to create. My next post will talk about the app I made.