1

I am trying to add WebAPI endpoints to an existing forms application but I am getting 404 errors.

I cannot use the Global.asax Application_Start() to register the api routes as suggested by Microsoft here because the current application already has a compiled customization of the Global class which inherits from HttpApplication and they did not mark any of the methods as virtual. doh!

I am trying to load the routes using an HttpModule. I am getting 404 errors for the following URL: https://example.com/webapplication/myapi/Authorize/User

Module code:

using System;
using System.Web;
using System.Web.Http;

public class MyHttpModule : IHttpModule
{
    private static bool HasAppStarted = false;
    private readonly static object _syncObject = new object();

    public void Init(HttpApplication context)
    {
        //https://stackoverflow.com/a/2416546/579148
        if (!HasAppStarted)
        {
            lock (_syncObject)
            {
                if (!HasAppStarted)
                {
                    GlobalConfiguration.Configure(config => RegisterRoutes.Load(config));
                    HasAppStarted = true;
                }
            }
        }
    }

    #region IDisposable Implementation
    #endregion
}

My registration class is in a standalone library:

using System.Web.Http;

public static class RegisterRoutes
{
    public static void Load(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute("DefaultApi", "myapi/{controller}");
    }
}

And my controller:

using System;
using System.Web.Http;
using MyLibrary.Models;

[Route("myapi/[controller]/[action]")]
public class AuthorizeController : ApiController
{
    [HttpGet, ActionName("User")]
    [AllowAnonymous]
    public WebUser GetUser()
    {
        WebUser user = new WebUser();
        user.Key = Guid.Empty.ToString();
        return user;
    }
}

And finally the web.config:

<configuration>
  <!--...-->
  <system.webServer>
    <!--...-->
    <modules>
      <add name="MyModule" type="MyLibrary.MyHttpModule" />
    </modules>
    <!--...-->
  </system.webServer>
  <!--...-->
<configuration>

The webapplication is its own IIS application (its own Global.asax and web.config). I am on Windows 10, CLR version v4.0, and Managed Pipeline is Integrated.

I've tried several other options described here, here, and here but have not had anything but 404s.

TIA!

jwatts1980
  • 7,254
  • 2
  • 28
  • 44
  • You are mixing up frameworks. `[Route("myapi/[controller]/[action]")]` is for [tag:asp.net-core] while your code appears to be for [tag:asp.net-web-api] – Nkosi Aug 17 '18 at 15:45

2 Answers2

1

You are mixing up frameworks. [Route("myapi/[controller]/[action]")] is for while your code appears to be for

A few suggested changes.

Module code can be simplified

GlobalConfiguration.Configure(RegisterRoutes.Load);

Since attribute routing is configured

 config.MapHttpAttributeRoutes();

Use the correct attributes on the APIController and action

[RoutePrefix("myapi/Authorize")]
public class AuthorizeController : ApiController {
    [HttpGet, ActionName("User")]
    [Route("user")] //GET myapi/authorize/user
    [AllowAnonymous]
    public IHttpActionResult GetUser() {
        WebUser user = new WebUser();
        user.Key = Guid.Empty.ToString();
        return Ok(user);
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thank you @Nkosi. I forgot to mention in my answer that I removed the attribute routes that I included in my question. I used the configure lambda because I will end up piping that `config` var into a couple of other calls. Also, I see that you are returning an action result type. What the pro/con on just returning the type directly? I have updated the serializers so that I the framework converts the types to JSON. I am currently getting a JSON response back from my API calls without using Action Result. – jwatts1980 Aug 17 '18 at 18:55
  • 1
    @jwatts1980 using the action result allows more flexibility for returning different http responses. using the plain object will always return HTTP 200 OK unless an exception is thrown. – Nkosi Aug 17 '18 at 19:30
  • Thank you for your answer. It was very helpful. I have updated to However, adding the `new { }` declaration to the `defaults` and `constraints` parameters as indicated by my answer was what finally made it start working. – jwatts1980 Aug 18 '18 at 20:00
0

Turns out the problem was in the MapHttpRoute() method call. It seems that the routing does not like not having a value for the defaults and constraints parameters. I updated the map call to this:

config.Routes.MapHttpRoute(
    name: "DefaultApi", 
    routeTemplate: Constants.ApiBaseRoute + "/{controller}/{action}",
    defaults: new {  }, 
    constraints: new {  }
);

I also had to add the action template parameter. And I removed the route attributes on the controller.

jwatts1980
  • 7,254
  • 2
  • 28
  • 44