Showing posts with label MVC. Show all posts
Showing posts with label MVC. Show all posts

2014-12-19

MVC Not Finding HttpPost Method

I have a MVC Web Form that when I posted, it was not finding the method decorated with the [HttpPost] attribute.

There in the form .cshtml page there was code like this:
@using (Html.BeginForm("Login", "Account"))
{
}

And code like this in the Account Controller

// Finding this
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
//...
}

// Wasn't finding this
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Login(LoginViewModel model, string returnUrl)
{
//...
}

When the form posted, it was going to the "GET" method instead of the "POST" method. After a bunch of debugging I finally figured out that the reason why is that I have an URL Rewrite rule on the web server that converts all URLs to lowercase for Search Engine Optimization (SEO) and logging consistency. This can also be done in the Web.Config file.

Because the form URL is in mixed case, it wasn't finding the correct method. All it takes to fix the problem is change the .cshtml form to have the parameters in lower case. Like this:

@using (Html.BeginForm("login", "account"))
{
}

2014-02-06

IsolatedStorage Exception: Unable to create the store directory 0x80131468

I've been beating my head against the wall for a few days trying to get IsolatedStorage to work on my web server. IsolatedStorage creates a private store for components some place on the web server, which varies depending on the login for the web server process that is used to create the isolated storage. A few of the places that it stores things, although there have been others in different versions of Windows:

  • C:\Windows\ServiceProfiles\LocalService\AppData\Local\IsolatedStorage
  • C:\Windows\SysWOW64\config\systemprofile\AppData\Local\IsolatedStorage
  • C:\Users\www.example.com\AppData\Local\IsolatedStorage

Each time that I tried to write to isolated storage, the code threw an exception System.IO.IsolatedStorage.IsolatedStorageException: Unable to create the store directory. (Exception from HRESULT: 0x80131468). What this told me was that the Web Server process user couldn't create the directory. For once, however, my Google-fu wasn't strong enough to actually solve the problem. I found on the web solutions like: go to the directory (which one?) where it was going to write and give the user (which one?) write permission on the directory, or give the process (which one?) enormous privilege for a little while so the directory could be created. I tried various permutations of these, without success.

Then I notice there is a property in the IIS configuration dialogs that is exactly what I need: Load User Profile, which by default is set to false. This property is found in IIS Manager. You select the application pool that controls your web site, and go to Advanced Settings. After turning this to true, IIS creates a directory for the web site under C:\Users. So if your web site is named www.example.com, it creates a directory C:\Users\www.example.com. When you write the Isolated storage, it is written under there under C:\Users\www.example.com\AppData\Local\IsolatedStorage.

2014-01-23

Using an ActionLink in a MVC Razor Declarative Helper

I have a declarative MVC Razor helper. I want to include an ActionLink inside it. This is currently a limitation on Razor helpers that you can't nest them. They keep promising to make it work in the "next version," but so far it isn't there.

So this doesn't work:

@helper DoesntWork(string text)
{
 @Html.ActionLink(text, "Index", "Bar")
}

The way around it takes advantage of the fact that ActionLink is merely a static method inside an extension class. You need to reference the actual method, and pass the html context as the first argument.

@helper Works(string text)
{
  @System.Web.Mvc.Html.LinkExtensions.ActionLink(

      ((System.Web.Mvc.WebViewPage)CurrentPage).Html, text, "Index", "Bar")
}

You should know that an extension method is just syntatic sugar that allows you to use Html.ActionLink("foo") instead of ActionLink(Html, "foo"). So if you can't use an it as an extension method, you can always fall back to just using it as a static method, if you can pass in the right first argument.

2014-01-22

Turning on Tracing in MVC (and Web Forms)

To turn on tracing in MVC or Web Forms Web Sites is very simple. Add this trace element to the XML in the Web.config at the root of the web site:

<configuration>
    <system.web>
        <trace enabled="true" pageOutput="false"
             requestLimit="40" localOnly="true"/>
    </system.web>
</configuration>

Then in your browser, browse to the root of the web site in IISExpress or full IIS, and add trace.axd. For example, if you site is at http://localhost:51500, then browse to http://localhost:51500/trace.axd. There is an enormous amount of information that will help debug information about your web site there.

The localOnly="true" attribute tells it to only perform tracing when browsing from localhost. In production, it should not perform tracing. There is a huge amount of information that hackers would like to get in the trace info, including information from recent hits to the web site from people other than yourself.

A couple of warnings: make sure you are messing with the Web.config in the root of the site. I spent an hour wondering why it wasn't working, then realized I was messing with the wrong Web.config! Doh! In MVC, don't mess with the normal routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); that you put into route configuration at the top of the site. You need that. If that isn't there, MVC tries to handle the request for the .axd file and IIS (which performs the tracing) won't.