29

I have an ASP.NET MVC 4 web site that includes Web API. The site is developed and tested with Visual Studio 2012 and .NET 4.5 on Windows 8 with IIS Express as web server. In this development environment everything works.

Now it is deployed on a Windows 2008 R2 (SP1) Server with IIS 7.5. .NET 4.0 and 4.5 are installed. The application pool is running with .NET 4.0 in integrated pipeline mode.

In this production environment the MVC web site works, Web API does not. For every request, no matter if GET or POST I get a 404 error. If I just enter a Web API Url in the browser (IE 9 opened locally on the server) to run a GET request I get a 404 page. If I issue a POST request from a Web API client application I get a 404 as well and the message:

No HTTP resource was found that matches the request URI

I've created a test website with MVC 4 and Web API as well and deployed it on the same server and the Web API works. Web API and MVC assemblies have the same version number in both projects.

Furthermore I have added the Web API Route Debugger to the application. If I use a valid route like http://myserver/api/order/12 I get the following result:

Route Debugger

For me this means that the correct route template Api/{Controller}/{Id} has been found and correctly parsed into a controller Order and Id=12. The controller (derived from ApiController) exists in the web assembly where also all MVC controllers are.

However, I don't know what the status 000 could mean and why there is no "Route selecting" section displayed (which is normally the case even if the assembly doesn't contain a single ApiController, see screenshots on the linked page above). Somehow it looks like no ApiController is found or even not searched for or the search fails silently.

The IIS log files don't show anything useful. Changing various application pool settings and using the same app pool for the test and the real application didn't help.

I am currently in the process to remove "features", configuration settings, third party assemblies, etc. from the application to bring it down to the small size of the test application in the end and hoping that at some point it starts to work.

Does somebody have a clue what the issue could be? Also any debugging or logging idea to possibly find the reason is very welcome.

Edit

Thanks to Darrel Miller's tip in the comments below I have integrated Tracing for ASP.NET Web Api.

For the (GET) request URL http://myserver/api/order/12 I get the following:

  • In development environment, successful (in short form):

Message: http://localhost:50020/api/order/12; Category: System.Web.Http.Request

Controller selection and instantiation...

Operator: DefaultHttpControllerSelector; Operation: SelectController; Message: Route="controller:order,id:12"; Category: System.Web.Http.Controllers

Operator: DefaultHttpControllerSelector; Operation: SelectController; Message: Order; Category: System.Web.Http.Controllers

Operator: HttpControllerDescriptor; Operation: CreateController; Message: ; Category: System.Web.Http.Controllers

Operator: DefaultHttpControllerActivator; Operation: Create; Message: ; Category: System.Web.Http.Controllers

Operator: DefaultHttpControllerActivator; Operation: Create; Message: MyApplication.ApiControllers.OrderController; Category: System.Web.Http.Controllers

Action selection, parameter binding and action invocation follows...

Content negotiation and formatting for result...

Operator: DefaultContentNegotiator; Operation: Negotiate; Message: Typ = "String" ... more

Disposing the controller...

Operator: OrderController; Operation: Dispose; Message: ; Category: System.Web.Http.Controllers

  • In production environment, not successful (in short form):

Message: http://myserver/api/order/12; Category: System.Web.Http.Request

Operator: DefaultHttpControllerSelector; Operation: SelectController; Message: Route="controller:order,id:12"; Category: System.Web.Http.Controllers

The whole part of controller activation, action selection, parameter binding, action invocation is missing and it follows content negotiation and formatting for the error message immediately:

Operator: DefaultContentNegotiator; Operation: Negotiate; Message: Type = "HttpError" ... more

Slauma
  • 175,098
  • 59
  • 401
  • 420
  • You may have to activate some IIS features in Server Roles > Internet Informations Services > World Wide Web Services. I don't know exactly what you need to activate but I think your problem can be here ! – Joffrey Kern May 29 '13 at 12:21
  • 1
    Try turning on the Web API tracing http://www.asp.net/web-api/overview/testing-and-debugging/tracing-in-aspnet-web-api – Darrel Miller May 29 '13 at 13:32
  • Looks like your WebAPI is not registered. what is going on with your `Global.asax` and `WebApiConfig`? Do they have some kind of conditional configuring inside it? – Liel May 29 '13 at 14:37
  • @DarrelMiller: Thanks! This was a great tip. However: I still don't have an explanation after looking at the trace output. I have added it to my question. – Slauma May 29 '13 at 15:40
  • 1
    @Liel: There is just the default route `Api/{controller}/{id}` with `id` set to default `RouteParameter.Optional`. As said, it works in dev environment. Would it work if Web API routes are not registered? – Slauma May 29 '13 at 15:45
  • 1
    I noticed that the Product Environment has the Neogiate operation with HttpError type. What error are you seeing when you make a request? Can you try setting `config.IncludeErrorDetailPolicy` as `Always` and see what it says? Also, I am suspecting there could be problem in resolving assemblies if all the required assemblies are not present in your production environment. To see if that's the case, you could do something that i mentioned in this post: http://stackoverflow.com/questions/16673851/getting-the-no-type-was-found-that-matches-the-controller-named-error-message/16674992#16674992 – Kiran May 29 '13 at 16:32
  • Have you correctly configured the bindings in IIS for the site? It may be a ridiculous suggestion but if the server is looking at the wrong folder because the bindings are incorrect you will of course get 404. – Stephen Kennedy May 29 '13 at 16:39
  • 1
    @KiranChalla: Big, big thank you!!! It was a missing assembly (see my answer below). Can you post your comment about assembly resolution and link to your post as an answer? I will accept it. BTW: Do you think there is an improvement of this "*We deliberately ignore all exceptions...*" part in the Web API source possible :) – Slauma May 29 '13 at 22:59
  • 1
    Glad that helped!!...yeah, I too think the experience should be improved further as many users seem to be hitting this problem...Could you please file an issue over here? : http://aspnetwebstack.codeplex.com/workitem/list/basic – Kiran May 30 '13 at 00:12
  • 1
    @KiranChalla: I've reported it here: http://aspnetwebstack.codeplex.com/workitem/1075 Thanks again :) – Slauma May 30 '13 at 11:10
  • I happenned to delete the global.asax file and that caused this issue. hope it helps someone\ – Saravanan Aug 21 '16 at 16:15

3 Answers3

23

Thanks to Kiran Challa's comment and source code from this answer I was able to figure out that an assembly (the ReportViewer 11 assembly for SQL Server Reporting Services) was missing on the production server.

Although no ApiController is in this assembly it seems to cause that controllers in assemblies - in this case my Web project's assembly - that are referencing the missing assembly are not found.

Apparently this behaviour is related to this piece of code from the Web API's DefaultHttpControllerTypeResolver source:

List<Type> result = new List<Type>();

// Go through all assemblies referenced by the application
// and search for types matching a predicate
ICollection<Assembly> assemblies = assembliesResolver.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
    Type[] exportedTypes = null;
    if (assembly == null || assembly.IsDynamic)
    {
        // can't call GetExportedTypes on a dynamic assembly
        continue;
    }

    try
    {
        exportedTypes = assembly.GetExportedTypes();
    }
    catch (ReflectionTypeLoadException ex)
    {
        exportedTypes = ex.Types;
    }
    catch
    {
        // We deliberately ignore all exceptions when building the cache. If 
        // a controller type is not found then we will respond later with a 404.
        // However, until then we don't know whether an exception at all will
        // have an impact on finding a controller.
        continue;
    }

    if (exportedTypes != null)
    {
        result.AddRange(exportedTypes.Where(x => IsControllerTypePredicate(x)));
    }
}

I don't know if it has to be this way and I am not quite convinced by the comment in the code but this catch ... continue block is rather silent about a possible problem and it took me a huge amount of time and frustration to find it. I even knew that the ReportViewer wasn't installed yet. I tried to install it and dependent assemblies but it was blocked by another running process on the server, so I decided to postpone the installation until I could contact the administrator and focus on MVC and WebAPI testing first - big mistake! Without Kiran's debugging code snippet I had never had the idea that the existence of a ReportViewer.dll could have anything to do with controller type resolution.

In my opinion there is room for improvement for the average developer like me who doesn't have a deeper knowledge about the inner workings of Web API.

After installing the missing ReportViewer.dll the problem disappeared.

Here are questions about the same symptom which might have the same reason:

Edit

I have issued a request for improvement on CodePlex:

http://aspnetwebstack.codeplex.com/workitem/1075

Edit 2 (Aug 11 '13)

The issue has been fixed for WebAPI v5.0 RC. See the link above to the workitem and its comments section for details.

Community
  • 1
  • 1
Slauma
  • 175,098
  • 59
  • 401
  • 420
6

Great resources in this answer, however, I was getting a 404 for what turned out to be a pretty silly reason:

  1. I created a WiX installer for my API project
  2. Installed it on a test VM (virtual machine)
  3. Requested for the a URI that the API should serve
  4. BANG! 404 :(

It turned out that I had missed packaging and deploying the Global.asax to the root of the virtual directory in my deployment. Adding this here for the benefit of goofies like myself :)

Sudhanshu Mishra
  • 6,523
  • 2
  • 59
  • 76
  • The last sentence was a clue for me, and I made it work by checking the "Pre-compile before...." when I publish, and it works like my local server. – Alex Jin Mar 27 '16 at 13:37
  • I feel like such an idiot! Thank you for your last paragraph - I forgot the global.asax file entirely!!! – ManselD Mar 04 '20 at 17:50
1

I had the same issue in a remote server, but when I execute on a localhost works fine.

My solution was:

<system.webServer>
    <modules>
        <remove name="UrlRoutingModule-4.0" />
        <add name="UrlRoutingModule-4.0" 
            type="System.Web.Routing.UrlRoutingModule" preCondition="" /> 
    </modules>
</system.webServer>

I hope, that this works for you.

ADreNaLiNe-DJ
  • 4,787
  • 3
  • 26
  • 35
framled
  • 388
  • 5
  • 18