7

I have the following action methods on a controller in an ASP.NET Web API project:

[Route("api/v2/project/{projectId}/stuff"), HttpGet]
public IHttpActionResult Get(int projectId)

[Route("api/v2/project/{projectId}/stuff/{id:guid}"), HttpGet]
public IHttpActionResult Get(int projectId, [FromUri] Guid id)

[Route("api/v2/project/{projectId}/stuff"), HttpPost]
public IHttpActionResult Post(int projectId, [Required] Stuff stuff)

[Route("api/v2/project/{projectId}/stuff/{id:guid}"), HttpPut]
public IHttpActionResult Put(int projectId, [FromUri] Guid blastId, Stuff stuff)

[Route("api/v2/project/{projectId}/stuff/{id:guid}"), HttpDelete]
public IHttpActionResult Delete(int projectId, [FromUri] Guid id)

Due to a javascript error, I made a DELETE request to

api/v2/project/1234/stuff/undefined

i.e. instead of a GUID for the id, I got the string "undefined". As far as I can tell, this shouldn't match any of my routes, but instead of a 404 Not found (or even 405 Method not allowed), I got a 200 OK as response.

I set a breakpoint in each of these action methods and repeated the request using Fiddler, but none of the breakpoints was hit. I also tried installing the WebApiRouteDebugger package from nuget, but we're using a custom controller factory which hooks things up through our DI container, so I couldn't get it to work at all. I even tried throwing the following exception from one of my globally registered filters:

throw new Exception(actionContext.ControllerContext.ControllerDescriptor.ControllerName +
" " + actionContext.ActionDescriptor.ActionName);

but the DELETE request still goes through to 200 OK (no requests to valid urls seem to do that).

How else can I troubleshoot this? What could be the root cause?

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • 1
    I created new Web APi project and added controller with your methods and routing for tests. Unfortunately works fine. For url 'api/v2/project/{project Id}/stuff/{id:guid}' I have 404 error. I think it's problem with incorrect routing in other controller or url with 'undefined' matches to other route. – Bartosz Czerwonka Feb 24 '15 at 19:54
  • @BartoszCzerwonka: Thanks a lot for taking the time to do that. The project is large-ish, which is why I didn't include all the routes in the entire project - however, I have checked carefully, and there are no routes at all in the project which match `api/v2/project/{projectId}/stuff/` except for in this controller. – Tomas Aschan Feb 25 '15 at 08:00
  • In addition, there's only a single one of them which should accept `DELETE` requests - and that is the one in which no breakpoint is hit. – Tomas Aschan Feb 25 '15 at 08:04
  • Can you post the code for your WebApiConfig.cs? – Josh Mar 04 '15 at 17:01
  • @Josh: I posted it (with some extra comments) [here](https://gist.github.com/tlycken/0980448e3f7546f176bb), to avoid cluttering up the question. – Tomas Aschan Mar 05 '15 at 11:22
  • Have you tried commenting out the DI route code and hitting the method? Another things you could try is moving 'config.MapHttpAttributeRoutes();' above the DI code and seeing if it gets hit then. At least you'll narrow down the possible problem. – Josh Mar 05 '15 at 15:31
  • Are you using some custom global filters? So does the `RegisterGlobalFilters` register something? Because the symptom that the controller is not called but the client is getting a response points to a filter or custom message/http handler which hijacks the request... – nemesv Mar 05 '15 at 20:53
  • Please check the response headers, in some curious condition, web api will return error information in the response header, this might be caused by some incorrect configuration, I had similar issue with yours, and I have to check the headers even server returns 200. – zhimin Mar 07 '15 at 01:28
  • Add `GlobalConfiguration.Configuration.Routes` to your Watch window and check whether any unexpected routes were registered. – Michael Liu Mar 08 '15 at 04:20

3 Answers3

3

In your Global.asax.cs file where your protected void Application_Start(object sender, EventArgs e) is, add the following:

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
    }

All server requests should come through here.

Add these using if not there.

using System.Web.Compilation;
using System.Reflection;

Then in the begin request call add this code to list out all of the active routes.

        string Items = "";
        IEnumerable<Assembly> Assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();

        foreach (Assembly FoundAssembly in Assemblies)
        {
            string AssemblyName = FoundAssembly.FullName;
            IEnumerable<TypeInfo> Types = FoundAssembly.DefinedTypes.Where(type => type != null && type.IsPublic && type.IsClass && !type.IsAbstract && typeof(ApiController).IsAssignableFrom(type));
            foreach (TypeInfo ControllerType in Types)
            {
                System.Web.Http.Controllers.ApiControllerActionSelector ApiControllerSelection = new System.Web.Http.Controllers.ApiControllerActionSelector();
                System.Web.Http.Controllers.HttpControllerDescriptor ApiDescriptor = new System.Web.Http.Controllers.HttpControllerDescriptor(new System.Web.Http.HttpConfiguration(), ControllerType.Name, ControllerType);
                ILookup<string, System.Web.Http.Controllers.HttpActionDescriptor> ApiMappings = ApiControllerSelection.GetActionMapping(ApiDescriptor);

                foreach (var Maps in ApiMappings)
                {
                    foreach (System.Web.Http.Controllers.HttpActionDescriptor Actions in Maps)
                    {
                        Items += "[ controller=" + ControllerType.Name + " action=" + ((System.Web.Http.Controllers.ReflectedHttpActionDescriptor)(Actions)).MethodInfo + "]";
                    }
                }
            }
        }

This will list all controllers and their signatures. If your URL does not fit into any of these you may have to expand the list to include non controller routes.

Brian from state farm
  • 2,825
  • 12
  • 17
  • Some other change in our project has made me unable to reproduce the problem since it occurred; however, this suggestion for troubleshooting is good enough that I'll be sure to use it should the problem re-emerge. Thanks a lot for the tip - I'm happy to award you a 1000% rep increase :) – Tomas Aschan Mar 10 '15 at 14:36
2

I had the same issue. In my startup.cs I had the method

app.Run(async context =>
       {
           context.Response.ContentType = "text/plain";
           await context.Response.WriteAsync("My Api");
       });

This was causing all routes to receive a 200 response along with the string. I only wanted to show this response on startup. This was the solution.

 app.MapWhen(ctx => ctx.Request.Path.Value == "/", StartupPage);

Along with a method in the same Startup class

  private void StartupPage(IAppBuilder app)
    {
        app.Run(async (context) =>
        {
            context.Response.ContentType = "text/plain";
            await context.Response.WriteAsync("My Api");
        });
    }

I only return the 200 OK message when a request is made on the base url.

joe.gonz
  • 159
  • 5
0

I'm am not sure of your routing but I think you have a routing that does not include actions in mapping:

api/v2/project/1234/stuff/undefined 

Check web api config for this: api/{controller}[/{action}]/{id}. I think you might hit another route action with your action(like a GET).

P.S. post your route config to update answer.

radu florescu
  • 4,315
  • 10
  • 60
  • 92