6

I'm having some troubles configuring WebApi routes in an asp.net WebForms app.

In the global.asax file, I have something like this:

void Application_Start(object sender, EventArgs e)
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
}

I have another file that has this code:

using System.Web.Http;

namespace WebConfig
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                        name: "DefaultApi",
                        routeTemplate: "api/{controller}",
                        defaults: new { id = System.Web.Http.RouteParameter.Optional });
        }
    }
}

Then, in the root, I have a folder called api and in that folder I have a file that contains a class that looks somewhat like that:

public class MyController : ApiController
{
    [Route("MyController")]
    [HttpPost]
    public HttpResponseMessage Post([FromBody]string value)
    {....}

And then finally, in my client code, I have this ajax call:

$.ajax({
    url: "/api/MyController",
    contentType: "application/json; charset=utf-8",
    type: "POST",
    dataType: "json",
    cache: "false",
    data: someData});

However, when I run this code, all I get is a 404 error. What do I need to change in the routing mechanism to make this work?

2
  • Btw, what do you mean by WebAPI for asp.net WebForms? Since vs2013 we use one type of project for all web applications(WebForms, MVC, WebAPI) Commented May 10, 2014 at 11:08
  • I'm not using a web application, just a web site. Commented May 10, 2014 at 11:18

8 Answers 8

2
+100

I think others were very close. Try this:

[RoutePrefix("api")]  // or maybe "api/", can't recall OTTOMH...
public class MyController : ApiController
{
    [Route("MyController")]
    [HttpPost]
    public HttpResponseMessage Post([FromBody]string value)

and then request /api/MyController

If this doesn't work, use RouteDebugger to analyze your routes, and why it rejects a match. Amend your question with what you see in RouteDebugger so I can trace down what is not matching.

Also, you may need to call MapHttpAttributeRoutes in your Register function - but not sure about that.


Edit

Now that I'm looking at it again, I think I see more issues with it.

First, let's start with the template:

Here is what you have (from your question):

config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}",
                    defaults: new { id = System.Web.Http.RouteParameter.Optional });

The odd part here is that your template does not have {id} segment, but is defined as optional. Sounds like it is missing from the template, and the route should be changed to:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = System.Web.Http.RouteParameter.Optional });

Note that you also removed default action - I'm not sure if MVC automatically uses convention method of locating a method called Post, but I suppose it does.

Second problem is that your method signature is (again from your question):

public HttpResponseMessage Post([FromBody]string value)

It defines Post that takes in a parameter named value, whereas your route defines parameter named id. Hence there is another mismatch. You can rename the variable or decorate (see below). Also, {id} is marked optional, but I believe (and here I don't remember exactly OTTOMH) you need to give default value to to value for those cases where {id} is not supplied, and so combining together:

public HttpResponseMessage Post([FromBody(Name="id")]string value = null)

This should fix it, although there may be more issues... but let's start with these.

Sign up to request clarification or add additional context in comments.

8 Comments

@frenchie What about RouteDebugger? Can you post its output - it would be very useful to get more data to figure it out. Also, to be sure, you checked/tried to call MapHttpAttributeRoutes, right?
All the code is posted; it's just a syntax problem that's solved in 10 seconds.
@frenchie I don't get your last comment. Half an hour ago you said that you tried a suggestion and it is still not working. Now you're saying that it is solved in 10 seconds. So you already have a solution (then you can post it as an answer), or do you still experience the issue? If the latter, then ability to have some debugging information will go a long way in helping you.
What I mean is that with the code I provided, the solution is a simple fix that probably just takes 10 seconds to implement if you know how to fix it. I'm still trying things on and off; basically I've written an app that uses an asmx web service: I have one web service that has just one method that I call using a POST and I'm looking to convert this web service to use WebAPI.
@frenchie Keep in mind that someone trying to help you on SO is doing so without the luxury of a debugger (for the most part), and obtaining some debugging information can help quite a bit to troubleshoot. Second, quite a lot of problems take 10 seconds to fix and yet take hours and days to figure out the issue (albeit knowing how to fix it in the first place, once knowing what it is).
|
1

Change url: "/api/MyController" to url: "/api/My"

2 Comments

Still getting a 404; if the controller is called MyController why would changing the ajax call to url: "/api/My" make a difference?
WebAPI by default would only look for the part before Controller suffix
1

Have you considered using Route attributes instead? I find them to be easier than route configs.

public class MyController : ApiController
{
    [Authorize]
    [Route("MyController")]
    [HttpPost]
    public HttpResponseMessage Post([FromBody]string value)
    {....}
}

perhaps you could additionally try configuring your routes inside of a dedicated RouteConfig class that mvc4 also supports. My route config setups look like this:

/// <summary>
/// The route config class
/// </summary>
public class RouteConfig
{
    /// <summary>
    /// Registers the routes.
    /// </summary>
    /// <param name="routes">The routes.</param>
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

4 Comments

I tried that approach but it doesn't work; I must be doing something wrong. Can you check the revised code I posted?
Besides the edit i just posted, the other thing you can do is to go to your base URL, "localhost:{port}/help", and see if you find your help page that will give you information about what routes you have configured. If you cant reach the help page, you may perhaps have other problems.
MapRoute is for MVC not for WebApi
While MapRoute is for MVC, WebApi will recognize it if you import System.Web.Mvc and use the MapRoute method on a RouteCollection object.
1

Use [RoutePrefix] on your Controller Class

[RoutePrefix("api/My")]
public class MyController : ApiController
{
    [Route("MyController")]
    [HttpPost]
    public HttpResponseMessage Post([FromBody]string value)
    {....}
}

Then you should be able to navigate to api/My/MyController

1 Comment

Actually, I want to navigate to api/MyController, not api/My/MyController
1

Change [Route("MyController")] to [Route("api/MyController")].

It will then work as you expect it to.

4 Comments

Hmm, that's strange, it worked correctly for me. I have a sample app I can send you if you wish; it might give you some ideas about what's wrong in your setup. I've been testing through Fiddler, although this shouldn't be much different from your js client. Could it be a problem on the client side?
I'm using the controller inside a website project, not a web application rpoject. May be that's why none of the answers solved it yet. Upvoted for effort.
In your question, you said you're having some troubles configuring WebApi routes in an asp.net WebForms app. So, this is what I did: I created a Web Forms application in VS2013 and ticked the "Web API" option. Then I copied and pasted the code in your question, compiled and ran it. I tested it with Fiddler by passing the same parameters as you do in your ajax call and, as you said, I got a 404. But after I changed the route as described in my reply, it then worked correctly. Mind you, I'm still pretty new to this technology, so I may be missing something. I'm sorry I couldn't help more.
It's not a web forms application, it's a website. In VS2013, go to File > New WebSite > and choose Asp.net Empty Web Site. It's not a web application.
1

Your RouteAttribute overrides config settings, so you have two options:

1) remove RouteAttribute and use URL: /api/my

2) keep RouteAttribute and use URL /MyController (in this case config settings ignored)

From comments i see, that you want to navigate to: api/MyController. So third option not tested, but I think it might work:

3) [Route("api/MyController")]

Comments

1

Ok, after over 2 weeks of wondering on and off why something seemingly simple wasn't working, I finally figured it out!!!!!

The problem is in this sentence:

Then, in the root, I have a folder called api and in that folder I have a file that contains ... the web API controller file

The solution turned out to be a 10-second fix:

Put the MyWebAPIController.cs file in the App_Code folder

The way I had it, the .cs file was in a root directory called /api because I had mistakenly thought that if you wanted to call the webservice from the client /api/MyController that you needed to create a directory that matched that path and then put the controller in that directory. Instead, you have to put the controller file in the App_Code directory.

LB2's answer did help me with the attributes, use both [RoutePrefix] and [Route]:

[RoutePrefix("api")]
public class MyController : ApiController
{
    [Route("MyController")]
    public HttpResponseMessage Post([FromBody]string value)

and then you can call it with this:

$.ajax({
    url: "/api/MyController",
    type:POST,
    ...});

Thank you all for looking into this issue!!

Comments

0

My challenges were slightly different. My setup is asp.net webforms project and using VS2013. I wanted to introduce Web Api. The curve ball was my web forms implementation is in VB.net.

I wanted to write my APIs in C# so I had to enable codeSubDirectories option via web.config.

<codeSubDirectories>
    <add directoryName="VBCode"/>
    <add directoryName="CSCode"/>        
</codeSubDirectories>

I created two folders (VBCode and CSCode) under App_Code folder respectively.

I then moved all my vb.net classes to VBCode sub directory.

Created UserController.cs under CSCode and wrote regular web api code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace WebAPIDemo
{

public class UserController : ApiController
{
    static IUserRepository _repo = new UserRepository();
    // GET api/<controller>

    public IEnumerable<Users> GetAllUsers()
    {
        return _repo.GetAllUsers();            
    }

    // GET api/<controller>/5
    public IHttpActionResult GetUserById(int id)
    {

        return Json(_repo.GetUserById(id));
    }

Modified the global.asax

<%@ Application Language="VB" %>
<%@ Import Namespace="System.Web.Routing" %>
<%@ Import Namespace="System.Web.Http" %>

<script RunAt="server">

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' Code that runs on application startup
        RouteTable.Routes.MapHttpRoute(
             "defaultApi",
            "api/{controller}/{action}/{id}",
            defaults:=New With {.id =     System.Web.Http.RouteParameter.Optional})

    End Sub

The api call worked with and without this decorator [RoutePrefix("api")].

Took me several hours to figure out this implementation.

Call 1:
http://localhost:60203/api/user/getuserbyid/2

Result:
{"Id":2,"FirstName":"Alan","LastName":"Wake","Company":"XYZ Corp","Email":"[email protected]","PhoneNo":"64649879"}

Call 2:
http://localhost:60203/api/user/getallusers

Result:
<ArrayOfUsers xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/WebAPIDemo">
<Users>
<Company>ABC</Company>
<Email>[email protected]</Email>
<FirstName>Madhur</FirstName>
<Id>1</Id>
<LastName>Kapoor</LastName>
<PhoneNo>65431546</PhoneNo>
</Users>
<Users>
<Company>XYZ Corp</Company>
<Email>[email protected]</Email>
<FirstName>Alan</FirstName>
<Id>2</Id>
<LastName>Wake</LastName>
<PhoneNo>64649879</PhoneNo>
</Users>
</ArrayOfUsers>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.