WebApi Controller Get Action Doesn't Work - asp.net-web-api

I'm having trouble with one of my WebApi2 controllers. While writing tests I've discovered the Get() is never hit instead it returns a 301 followed by 403. Oddly if I hit Get(id) the second action speaks up and does its job, but I can never hit the Get() action. If I rename the controller it works properly, but sadly, I cannot rename ModelsController to ModelController as I would like since there is an existing user base expecting that name. This is a rewrite of an existing Api that was done in MVC2 I think. All the other controllers work great, just not this one.
Any thoughts as to how to debug this? Or what I may have missed?
Other notes:
The routing configuration is default.
There is no other ModelsController to be found
Below is a simplified version of my code, and the problem remains...
using System.Collections.Generic;
using System.Web.Http;
using TW.Api.Business.Services.Models;
namespace TW.Api.Business.Services.Controllers
{
public class ModelsController : ApiController
{
public string Get()
{
return null;
}
public string Get(string id)
{
return null;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Routing;
namespace tw.api.business
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "Api_GetWithChild",
routeTemplate: "{controller}/{id}/{action}/{childId}",
defaults: new { id = RouteParameter.Optional, action = "Get", childId = RouteParameter.Optional },
constraints: new { httpMethod = new HttpMethodConstraint("GET") });
config.Routes.MapHttpRoute(
name: "Api_Post",
routeTemplate: "{controller}/{id}/{action}/{childId}",
defaults: new { id = RouteParameter.Optional, action = "Post", childId = RouteParameter.Optional },
constraints: new { httpMethod = new HttpMethodConstraint("POST") });
config.Routes.MapHttpRoute(
name: "Api_Put",
routeTemplate: "{controller}/{id}/{action}/{childId}",
defaults: new { id = RouteParameter.Optional, action = "Put", childId = RouteParameter.Optional },
constraints: new { httpMethod = new HttpMethodConstraint("PUT") });
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
//config.EnableSystemDiagnosticsTracing();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace tw.api.business
{
public class RouteConfig
{
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 }
);
}
}
}

Looking at your route config, there are a few issues
The constrained routes are not really necessary
The id being optional in a middle of a route pattern doesn't really work, your optional items should come at the end.

When creating methods for web-API end-points, you must follow some naming conventions for automatically mapping of method-to-api. Or you can override that using HTTP action and Route attributes
Follow either of the following arrangements (note the 'Model' and 'Models' in method names):
public class ModelsController : ApiController
{
public string GetModels()
{
return null;
}
public string GetModel(int id)
{
return null;
}
}
Or, use the route attributes:
public class ModelsController : ApiController
{
[HttpGet]
[Route("api/Models")]
public string GetModels()
{
return null;
}
[HttpGet]
[Route("api/Models/{id}")]
public string GetModel(string id)
{
return null;
}
}
Ref: Routing and Action Selection

Related

Web API error - Server Error in '/api' Application

After publishing Web API to IIS, which is a child of an AngularJs IIS site, I can reach 'https://localhost/api' and see all endpoints; but when I try to reach some specific endpoint with a GET request, I get an error
Server Error in '/' Application
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);
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
[RoutePrefix("api/branches")]
public class BranchesController : ApiBaseController
{
[HttpGet]
[Route("getBranches")]
public async Task<JsonResult<List<BranchDto>>> GetActiveBranches()
{
var branches = new List<BranchDto>();
var models = await WarehouseUnitOfWork.BranchRepository.GetActiveBranches();
if (models != null && models.Count > 0)
{
branches.AddRange(AutoMapper.Mapper.Map<List<Branch>, List<BranchDto>>(models));
}
return Json(branches, new JsonSerializerSettings
{
ContractResolver = new WarehouseCustomContractResolver()
});
}
}
Any ideas how to solve this?
The solution for my case was to deploy the Frontend into the main IIS site, and inside it create an application called v1 for the Backend.
Then within my angularJS I defined the Production app to make the http requests to /v1/api instead of /api.

Web api call works locally but not on Azure

I have same issue as per following questions and tried answer but never resolve issue
Web api interface works locally but not on Azure
Web API interface works locally but gets 404 after deployed to Azure Website
and many more similar type of...
When i tried to call api it says 404 Not Found
my WebApi.config file
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "versionApi",
routeTemplate: "api/{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
API Controller
[Authorize]
[RequiresSSL]
[RoutePrefix("api/v2/Configuration")]
public class ConfigurationAPIv2Controller : ApiController
{
[Dependency]
public IConfigurationServicev2 configurationService { get; set; }
[Dependency]
public IAccountService accountService { get; set; }
#region testapi
[Route("getstring")]
[HttpGet]
public IHttpActionResult getstring()
{
return Ok("Success");
}
[Route("putstring")]
[HttpPut]
public IHttpActionResult putstring()
{
return Ok("Success");
}
#endregion
And Folder Structure is like :
i got follwowing issue for both get and Put method
404 error might caused by route issue. Since you are using route attribute for your Web API. Please make sure GlobalConfiguration.Configure(WebApiConfig.Register); is above other code.
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
And config.MapHttpAttributeRoutes(); code is above other routes configuration.
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "versionApi",
routeTemplate: "api/{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In addition, try to delete following code in your Controller to test whether it is related to the dependent injection module.
[Dependency]
public IConfigurationServicev2 configurationService { get; set; }
[Dependency]
public IAccountService accountService { get; set; }
If it also can't work for you. You could get the detail error message from web server after setting IncludeErrorDetailPolicy property in WebApiConfig class.
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
Visual Studio does not add a default.html page to wwwroot and IIS does.
Just add default.html page to wwwroot in your project, then re-deploy to Azure.

web api2 RESTful GET not returning get by id

not quite sure what I am missing
standard webapiconfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace MROVendorWebAPI
{
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 }
);
}
}
}
pretty basic controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using MROVendorWebAPI.Models;
namespace MROVendorWebAPI.Controllers
{
public class VSMRICSController : ApiController
{
public IEnumerable<DB2VsmRicRecord> GetDB2VsmRicRecords()
{
string ErrorDescription = "";
DB2VsmRicCollection myDB2VsmRicCollection = new DB2VsmRicCollection();
if (myDB2VsmRicCollection.CreateCollection(ref ErrorDescription))
{
return myDB2VsmRicCollection.oDB2VsmRicList;
}
return null;
}
public DB2VsmRicRecord GetDB2VsmRicRecord(string ric)
{
string ErrorDescription = "";
DB2VsmRicCollection myDB2VsmRicCollection = new DB2VsmRicCollection();
if (myDB2VsmRicCollection.CreateCollection(ref ErrorDescription))
{
return myDB2VsmRicCollection.oDB2VsmRicList.FirstOrDefault(p => p.Ric == ric);
}
return null;
}
}
}
using /api/VSMRICS works correctly and returns the entire collection
and using /api/VSMRICS?RIC=SAH works correctly and returns just one record however /api/VSMRICS/SAH returns the entire collection. not sure why that is do I need to add some sort of routing to this method in the controller?
duh never mind it needs to be
public DB2VsmRicRecord GetDB2VsmRicRecord(string id)
In conventional routing it search for the RouteParameter which is mentioned in route as id.
Here you can also change the route like below:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{ric}",
defaults: new { id = RouteParameter.Optional }
);
For supporting your API:
public DB2VsmRicRecord GetDB2VsmRicRecord(string ric)
{
string ErrorDescription = "";
DB2VsmRicCollection myDB2VsmRicCollection = new DB2VsmRicCollection();
if (myDB2VsmRicCollection.CreateCollection(ref ErrorDescription))
{
return myDB2VsmRicCollection.oDB2VsmRicList.FirstOrDefault(p => p.Ric == ric);
}
return null;
}

web api - message handler attribute routing

Does anyone know whether message handler can work simultaneously with attribute routing in Web API 2.x? I got a custom message handler to work using conventional routing, then after adding attribute routing, it stops working. I am not sure whether it's not supported or if I had misconfigured something. Any help is greatly appreciated.
1) before attribute routing
--- WebApiConfig.cs code snippet (simplified)----
config.Routes.MapHttpRoute(
name:"DefaultApi",
routeTemplate: "api/{controller}",
defaults: null,
constraints: null,
handler: my-message-handler-object
);
--- MyController.cs code snippet (simplified)----
public class MyController : ApiController
{
[HttpGet]
public IHttpActionResult CheckInServices(...)
{
...
}
}
2) after attribute routing
--- WebApiConfig.cs code snippet (simplified)----
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name:"DefaultApi",
routeTemplate:"api/vendor",
defaults: new { controller = "Users" },
constraints: null,
handler: my-message-handler-object
);
}
--- MyController.cs code snippet (simplified)----
[RoutePrefix("api/vendor/{vendorID:long}/service")]
public class MyController : ApiController
{
[HttpPost]
[Route("{serviceID:long}")]
public IHttpActionResult CheckInServices(...)
{
...
}
}
Thanks,
Cody
Global message handlers will work - just set it up on start up.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new YourAuthenticationHandler());
}
}
I'm unsure if per route Message Handlers work with Attribute Routing.

Web API - supported verbs?

I am putting together a talk for a local Code Camp and am trying to understand the nuances of HTTP Verbs in ApiController. Several things about ApiController significantly changed between Beta, RC and final release, and the advice on how you can set this up is conflicting and sometimes wrong.
Assuming I am just leaving the standard routing in WebApiConfig:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
(since you can really put things on their head if you add in an {action} parameter here)
I understand how the convention is working for simple Crud calls like:
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
Or that you can change these as long as they start with the verb name:
// GET api/values/5
public string GetMyStuff(int id)
{
return "value";
}
However, the initial spec says that ApiController supports Get, Put, Post and Delete.
Yet I can add methods for:
public void HeadOfTheClass()
{
}
Which works for a Head verb, but I can’t add methods for the obscure or non-existent verbs:
public void MKCOL()
{
}
public void Bubba()
{
}
What is the full list of Native “Supported” verbs?
I can however add support for these methods by using the AcceptVerb attribute:
[AcceptVerbs("MKCOL")]
public void MKCOL()
{
}
[AcceptVerbs("Bubba")]
public void Bubba()
{
}
This also works, or for any “defined” verb use the Http attributes:
[HttpHead]
public void HeadOfTheClass()
{
}
[HttpGet]
public void Bubba()
{
}
Which is correct or preferred? (There was also attributes like [GET] and [POST] as well, are these deprecated?)
Are [HttpBindNever] and [NonAction] equivalent?
I love open source. :)
From ReflectedHttpActionDescriptor:
private static readonly HttpMethod[] _supportedHttpMethodsByConvention =
{
HttpMethod.Get,
HttpMethod.Post,
HttpMethod.Put,
HttpMethod.Delete,
HttpMethod.Head,
HttpMethod.Options,
new HttpMethod("PATCH")
};

Resources