I am using VS 2017 community. I have been building web api s for years. But something must have changed as I cannot get the simplest example to work.
I have a simple controller in the controller folder
public class TestApi : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
I have the necessary code in application start:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
But when I try and test the web api with a get like:
http://localhost:54014/api/testapi
I always get an xml message
Error
Message
No HTTP resource was found that matches the request URI
'http://localhost:54014/api/testapi'.
/Message
MessageDetail
No type was found that matches the controller named 'testapi'.
/MessageDetail
/Error
Here is the WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
I am a couple of hours into head scratching on this. As I say I have built many MS web api implementations and this one has me baffled.
You should add Controller suffix to your class name.
public class TestApiController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
When the app starts, the asp.net mvc frameworks looks for classes (which are inherited from ApiController or Controller) with this suffix and register them when building the route table. When a request comes to your app, the framework again look into the route table and direct the request to the corresponding controller and action method.
Make this change, rebuild your project and try again.
In addition to the already provided answer (which is correct by the way) you can also consider using attribute routing over convention-based routing, where in this case it would not matter what the name of the controller is.
You already have it enabled Based on the WebApiConfig.cs and
config.MapHttpAttributeRoutes();
So now it is just a matter of putting the attributes where needed
[RoutePrefix("api/testapi")]
public class TestApi : ApiController {
[HttpGet]
[Route("")] //Matches GET api/testapi
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
}
Reference: Attribute Routing in ASP.NET Web API 2
Related
Was coaching a colleague on Web API. Just trying to run the default Web API template out of the Visual Studio box, as it were...not even doing anything fancy. A default (Get) call to my API (api/{controller}/{id}) keeps returning the MVC home view instead of the contents of the Get method in my API controller.
namespace WebAPI.Controllers
{
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
...
namespace WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
namespace WebAPI
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
I've looked far and wide across the 'Net but can't find anything address my specific problem.
I expected the default value1, value2, etc. Instead, the MVC home view keeps returning instead.
I am using Owin self-hosting with WebAPI2.
I have two controller classes and using attribute routing.
One of them has following signature:
[RoutePrefix("api/v1/devices")]
public class DeviceController : ApiController
{
[HttpPost]
[Route("")]
public async Task<HttpResponseMessage> DevicePresence()
{
...
}
[HttpGet]
[Route("updates/{deviceID}")]
public HttpResponseMessage GetDeviceUpdates(string deviceID)
{
...
}
}
This one is working fine and action methods get triggered.
The other Controller has below signature:
[RoutePrefix("device/class")]
public class RemoteController : ApiController
{
[HttpGet]
[Route("remotehost")]
public HttpResponseMessage RemoteHost()
{
...
}
[HttpGet]
[Route("version")]
public HttpResponseMessage GetVersion()
{
...
}
}
When I try to connect to any of these I get 503 (Service Unavailable) response.
My Startup class is initialized as below:
public class Startup
{
public static void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.EnsureInitialized();
app.UseWebApi(config);
}
}
I tried to use the conventional routing as well but still the same problem.
config.Routes.MapHttpRoute(
name: "RemoteApi",
routeTemplate: "device/{controller}/{action}"
);
public class ClassController : ApiController
{
public HttpResponseMessage GetVersion()
{
...
}
}
This is also throwing 503 status code
If I change the Route prefix in the second controller as below then it's working:
[RoutePrefix("api/v1/device/class")]
public class RemoteController : ApiController
{
...
}
As per my requirement, I couldn't change the endpoints.
I am not sure what's wrong here and any help would be much appreciated.
Your RemoteController throws an exception because you do not follow naming conventions of WEB API.
Your RemoteHost() method which is a get method has to have a "Get" prefix, so its name is supposed to actually be GetRemoteHost().
That should solve it.
In case you want to alter the naming conventions, you can modify route definitions in the global.asax file:
Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
Hope that helped.
I realized it was not a problem with webapi or routing.
Actually, I was adding a firewall exception for the URL's and somehow it was not getting removed from firewall settings and keeps an entry in DACL.
I removed this from cmd prompt and now everything works fine.
Sorry for bothering...
Being a noob in MVC web api there is probably something very obvious I'm missing..
However, In my ProjectController I have the following method with attributes (I know this kind of method should be POST but just testing easily...):
[Route("api/projects/archive/{id:int}")]
[HttpGet]
public void Archive(int id)
{
_projectService.Archive(id);
}
However, when I open my URL such as:
http://localhost:49923/api/projects/archive/1
The page simply redirects to the current URL, and the archive method is not called. I also have a breakpoint at the method to verify it's not hit.
My best guess is I also have to update my web api route config which is the default, but I just assumed the route attribute was enough?
Here is my web api route config:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional});
config.Formatters.JsonFormatter.SupportedMediaTypes
.Add(new MediaTypeHeaderValue("text/html"));
}
What am I doing wrong here? :-)
EDITS:
Clearification 1 - my ProjectController:
public class ProjectsController : ApiController
{
private ProjectService _projectService;
public ProjectsController()
{
_projectService = new ProjectService();
}
[Route("api/projects/archive/{id:int}")]
[HttpGet]
public void Archive(int id)
{
_projectService.Archive(id);
}
}
Clearification 2 - redirect:
So lets say I stand on the homepage (/). I then go to the URL "http://localhost:49923/api/projects/archive/1", it will just reload page and leave my back at the home page.
The Web API config is configured correctly.
Ensure that the controller and the action are constructed properly
public class ProjectController : ApiController {
//...other code removed for brevity
[HttpGet]
[Route("api/projects/archive/{id:int}")]//Matches GET api/projects/archive/1
public IHttpActionResult Archive(int id) {
_projectService.Archive(id);
return Ok();
}
}
Its bit late to answer but hope you find it useful,
Many times the way how we write the code help us find the solution to the problem,
As Nkosi already answered, that the constructing controller and the action method properly will resolve the issue.
Its always helpful to check the method first rather then looking at the route.config because by default it will be the same unless you provide your custom attribute.
I have two web API project DLLs in one solution.
Structure of my Project Solution:
In my solution, the projects are located as follows:
1) Base Solution
2) Base Web API
3) Module Web API
Hence, my solution is something like a BASE solution which contains many MODULES. Each Modules can contain its own Web APIs. Also, my Base Solution contains its own Web API
This is our structure.
My Problem:
It is working fine in my local run solution. When I host it to the IIS, it is working for few hours and then it stops working by throwing the error message "Error 404 found". When I try to access through URL directly which is something like "http://127.0.0.1:51/Products/Get", not working.
Visual Studio version:
Visual Studio Professional - 2015
IIS Version:
IIS - 7.5.7600
My approach:
I have a simple project which simulates this scenario. It has the same problem with my original project.
Web API For Base Module:
WepApiConfig under App_Start:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Base API Controller:
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
WebApiConfig.cs For Module Web API:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
//config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultModuleApi",
routeTemplate: "api/module/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Module API Controller:
public class ModulesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
NOTE from the above code:
The difference between the two APIConfig files is :
For Module Code:
routeTemplate: "api/module/{controller}/{action}/{id}"
For Base Code:
routeTemplate: "api/{controller}/{action}/{id}"
Global.asax:
namespace BaseSln
{
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//GlobalConfiguration.Configure(WebApiConfig.Register);
var typesWithMyAttribute =
from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetLoadableTypes().Where(t1 => t1.Name == "WebApiConfig"
&& t1.GetMethod("Register") != null
&& t1.GetMethod("Register").IsStatic)
select new { Type = t, MethodInfo = t.GetMethod("Register") };
//register all the Routes
foreach (var type in typesWithMyAttribute)
{
var mi = type.MethodInfo;
Action<HttpConfiguration> action = null;
try
{
action = Delegate.CreateDelegate(typeof(Action<HttpConfiguration>), mi) as Action<HttpConfiguration>;
}
catch
{
//ignore the errors for now... should be handled somewhere else or logged.
}
if (action != null) GlobalConfiguration.Configure(action);
}
}
}
}
What I tried with the above project:
After hosting in IIS, I tried to access the path which is something like this:
For Base API:
http://localhost:51600/api/Values/Get
Returns:
value1
value2
For Module API
http://localhost:51600/api/Modules/Get
Returns:
value1
value2
My problem:
After sometime, when I try to access the same link, I am unable to get that. It says
status 404. Not Found
I have been working on this issue for 3 days, and I couldn't resolve the problem.
I have searched many articles in stackoverflow, but no luck.
Kindly help me to get rid off from this.
Thanks.
Can you check the GlobalConfiguration.Configuration.Routes in the Base Solution if you have all the routes for both the Base Web API and the Module Web API?
I am developing a web api but it can not hit it. Error shows 404 not Found.
Web Api
using Atea.Azure.ApiMangement.Business;
using System.Web.Http;
namespace Azure_API_Delegation_Portal.Controllers
{
[RoutePrefix("api/apim")]
public class ApimController : ApiController
{
private readonly ISubscriptionService _subscriptionService;
[HttpGet]
[Route("{string:productId}")]
public bool GetProductSubscribe(string productId)
{
return _subscriptionService.IsSubscribed(productId);
}
}
}
How I call an API https://localhost:44300/api/apim/ldkjfk232
Web API Route
using System.Web.Http;
namespace Azure_API_Delegation_Portal
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
Image
I am missing this line of code in Application_Start() function in "Global.asax" file.
GlobalConfiguration.Configure(WebApiConfig.Register);
Fix your route template. It is string by default so no need for the string constraint
//GET api/apim/ldkjfk232"
[HttpGet]
[Route("{productId}")]
public bool GetProductSubscribe(string productId)
Also note that the constraint goes after the placeholder name like this example
[Route("{paramaterName:int}")]
Read more about attribute routing here : Attribute Routing in ASP.NET Web API 2
It will show you how to properly configure your web api.