Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Web Api returns 200 OK when it should return 404

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?

like image 447
Tomas Aschan Avatar asked Feb 24 '15 16:02

Tomas Aschan


2 Answers

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.

like image 163
Brian from state farm Avatar answered Nov 20 '22 17:11

Brian from state farm


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.

like image 24
joe.gonz Avatar answered Nov 20 '22 16:11

joe.gonz