add web api route to exists web site cannot modify global asax - asp.net

I have a web api project and i can run it locally, but when i publish dll´s to bin folder in the web site, is not taking the route config. I.ve been unable to modify or replace the global asax becasue it has its own implementation. where is the best place to set mi web api route config, how can i do it with an httpmodule?
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
UPDATE:
i added a httpmodule, and is not mapping the route, is sending http 404:
public void Init(HttpApplication context) {
try
{
AreaRegistration.RegisterAllAreas();
var routes = RouteTable.Routes;
// Controllers with Actions
// To handle routes like '/api/controller/route'
routes.MapHttpRoute(
name: "ControllerActionAndId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = #"^\d+$" }
);
// Controllers with Actions
// To handle routes like '/api/controller/route'
routes.MapHttpRoute(
name: "ControllerAndAction",
routeTemplate: "api/{controller}/{action}"
);
// Controller with ID
// To handle routes like '/api/controller/1'
routes.MapHttpRoute(
name: "ControllerAndId",
routeTemplate: "api/{controller}/{id}",
defaults: null,
constraints: new { id = #"^\d+$" } // Only integers
);
// Controller Only
// To handle routes like '/api/controller'
routes.MapHttpRoute(
name: "ControllerOnly",
routeTemplate: "api/{controller}"
);
}
catch { }
}

can you change the webconfig if you can't change the global assax?
with a httpmodule you would get somthing like
<system.webserver>
<modules>
<add name="WebApiStartModule" type="myproject.WebApiStartModule, assembly"/>
</modules>
<system.webserver>
public class WebApiStartModule : IHttpModule
{
public void Init(HttpApplicationContext) {
var routes = RouteTable.Routes
routes.Clear()
//initialize routing here
}
}

Related

using Web Api in Webforms return 404 error

I have an ASP.NET Webforms web site that includes Web API. The site is developed and tested with Visual Studio 2013 and .NET 4.5 on Windows 8 with IIS Express as web server.
I have added the Web Api controller in the root directory that is defined as follows:
[RoutePrefix("api")]
public class ProductsController : ApiController
{
Product[] products = new Product[]
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
};
[Route("ProductsController")]
[HttpGet]
public IEnumerable<Product> GetAllProducts()
{
return products;
}
public Product GetProductById(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return product;
}
public IEnumerable<Product> GetProductsByCategory(string category)
{
return products.Where(
(p) => string.Equals(p.Category, category,
StringComparison.OrdinalIgnoreCase));
}
}
The Global.asax looks like this:
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional });
}
}
I have included these 2 lines in my web.config file
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
When I make the get request with the following Url : http://localhost:5958/api/products, I get HTTP Error 404.0. I have tried different solutions but nothing works. Is there something that I'm missing? How do I rectify this problem?
Thanks in advance.
You mixing up some of your convention-based and attribute routing for your web api.
Attribute Routing in ASP.NET Web API 2
If you are going to use attribute routing then you need to properly add the routes to your controller.
[RoutePrefix("api/products")]
public class ProductsController : ApiController
{
//...code removed for brevity
//eg: GET /api/products
[HttpGet]
[Route("")]
public IEnumerable<Product> GetAllProducts(){...}
//eg: GET /api/products/2
[HttpGet]
[Route("{id:int}")]
public Product GetProductById(int id){...}
//eg: GET /api/products/categories/Toys
[HttpGet]
[Route("categories/{category}")]
public IEnumerable<Product> GetProductsByCategory(string category){...}
}
Now that you have your routes properly defined, you need to enable attribute routing.
WebApiConfig.cs
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
// Enable attribute routing
config.MapHttpAttributeRoutes();
// Convention based routes
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
make sure to update the Global.asax code to the following:
public class Global : HttpApplication {
void Application_Start(object sender, EventArgs e) {
//ASP.NET WEB API CONFIG
// Pass a delegate to the Configure method.
GlobalConfiguration.Configure(WebApiConfig.Register);
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
WebForm's RouteConfig
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings);
}
}
resource:
Can you use the attribute-based routing of WebApi 2 with WebForms?.
Based on your code,you didn't include routes for webapi. If you install Webapi from Nuget, you can find the WebApiConfig file under App_Start which contains the Route configuration for WebApi.
If not create a new route config file for webapi
using System.Web.Http;
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 }
);
}
}
Use this static method in your Global.ascx.
using System.Web.Http;
void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(WebApiConfig.Register);
// Code that runs on application startup
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}

Why isn't my ASP.NET API route being called, is my URL not correct?

I have a new ASP.NET MVC 5 application.
I added an /api folder in my controllers folder and added a MVC2 API controller.
My global.asax has:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
My controller looks like:
public class UsersController : ApiController
{
[HttpGet]
[ActionName("User")]
public IToken GetUser()
{
return new User();
}
}
Now I get a 404 resource cannot be found error when I go to:
http://localhost:53323/api/users/user
http://localhost:53323/api/users/getuser
What could the problem be?
Update
My MVC route config:
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 }
);
}
My API route config:
config.MapHttpAttributeRoutes();
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Seems like you are missing correct registration in your Application_Start().
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration); // This should be above the default route registration.
RouteConfig.RegisterRoutes(RouteTable.Routes);
//GlobalConfiguration.Configure(WebApiConfig.Register); // Remove this line.
}
Try using attribute routing on your controller and action methods.
Example
[RoutePrefix("api/users")]
public class UsersController : ApiController
{
[HttpGet]
[Route("user")]
public IToken GetUser()
{
return new User();
}
}

How should I handle MVC and WebAPI routing for a SPA with one index.cshtml page?

I have a single page application that has one MVC file index.cshtml which
is served by the Home controller and Index method. All the logon, logoff
and data requests are served by WebAPI. Can someone confirm if I am setting up
my routing correctly. Here is what I have:
RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?favicon.ico(/.*)?" });
routes.MapRoute("DefaultRedirect",
"",
new { controller = "Home", action = "Index" }
);
routes.MapRoute(
"catchall",
url: "{*url}",
defaults: new { controller = "Home", action = "Index" });
}
WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: ApiControllerOnly,
routeTemplate: "api/{controller}"
);
config.Routes.MapHttpRoute(
name: ApiControllerAndId,
routeTemplate: "api/{controller}/{id}",
defaults: null, //defaults: new { id = RouteParameter.Optional } //,
constraints: new { id = #"^\d+$" } // id must be all digits
);
config.Routes.MapHttpRoute(
name: ApiControllerAction,
routeTemplate: "api/{controller}/{action}"
);
config.Routes.MapHttpRoute(
name: ApiControllerActionAndId,
routeTemplate: "api/{controller}/{action}/{id}",
defaults: null, //defaults: new { id = RouteParameter.Optional } //,
constraints: new { id = #"^\d+$" }
);
}
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
WebApiConfig.CustomizeConfig(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
What I am not sure of is the first file that handles MVC routes. Do I need to have
just one route that makes everything go to Home Controller and Index method.
Also should I in the MVC routing ignore those requests that start with api ?
You should not serve your SPA page as index.cshtml (because that means that it is being compiled to the respective class on the server that will serve the response)
It should be served as index.html and all data that it requires should be fetched by AJAX calls to the server.

ASP.Net Web Api routing issue with ActionName

I am working with an ASP.Net Web Api project on Web Developer Express 2010. The routing config is defined in WebApiConfig.cs as:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi3",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = RouteParameter.Optional,
id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi4",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = RouteParameter.Optional }
);
}
An API Controller called "GCURObservationController" has an action as:
[HttpGet, ActionName("retrieveCuringMaps")]
public IList<SimpleCuringMapsModel> retrieveCuringMaps()
{
... ...
return jsonCuringMapModels;
}
The project was compiled and run successfully. However, I had to go to
http://localhost:2061/api/GCURObservation/retrieveCuringMaps/0
to get the action triggered (action name followed by any integer), rather than what I expected to be
http://localhost:2061/api/GCURObservation/retrieveCuringMaps
That means an arbitrary integer had to follow the action name to get it right. Otherwise, the error was returned. I don't want this action to be triggered with any param.
{"Message":"The request is invalid."}
How to get the second URL to work? Thanks
Cheers,
Alex
If you are using Web API 2, following is one solution you could use. In the below example, I am using attribute routing and conventional routing together in one controller. Here all the actions except GetCustomerOrders are reached via conventional route "DefaultApi".
In general the idea here is not new, that is...even without Web API 2's attribute routing, you could define routes for each individual action of a controller in the global route table, but attribute routing makes this process easier as you can define routes directly and near to the action.
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
[RoutePrefix("api/customers")]
public class CustomersController : ApiController
{
public IEnumerable<Customer> GetAll()
{
}
public Customer GetSingle(int id)
{
}
public void Post(Customer customer)
{
}
public void Put(int id, Customer updatedCustomer)
{
}
public void Delete(int id)
{
}
[Route("{id}/orders")]
public IEnumerable<Order> GetCustomerOrders(int id)
{
}
}

Web API Routing - api/{controller}/{action}/{id} "dysfunctions" api/{controller}/{id}

I have the default Route in Global.asax:
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
I wanted to be able to target a specific function, so I created another route:
RouteTable.Routes.MapHttpRoute(
name: "WithActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
So, in my controller, I have:
public string Get(int id)
{
return "object of id id";
}
[HttpGet]
public IEnumerable<string> ByCategoryId(int id)
{
return new string[] { "byCategory1", "byCategory2" };
}
Calling .../api/records/bycategoryid/5 will give me what I want.
However, calling .../api/records/1 will give me the error
Multiple actions were found that match the request: ...
I understand why that is - the routes just define what URLs are valid, but when it comes to function matching, both Get(int id) and ByCategoryId(int id) match api/{controller}/{id}, which is what confuses the framework.
What do I need to do to get the default API route to work again, and keep the one with {action}? I thought of creating a different controller named RecordByCategoryIdController to match the default API route, for which I would request .../api/recordbycategoryid/5. However, I find that to be a "dirty" (thus unsatisfactory) solution. I've looked for answers on this and no tutorial out there on using a route with {action} even mentions this issue.
The route engine uses the same sequence as you add rules into it. Once it gets the first matched rule, it will stop checking other rules and take this to search for controller and action.
So, you should:
Put your specific rules ahead of your general rules(like default), which means use RouteTable.Routes.MapHttpRoute to map "WithActionApi" first, then "DefaultApi".
Remove the defaults: new { id = System.Web.Http.RouteParameter.Optional } parameter of your "WithActionApi" rule because once id is optional, url like "/api/{part1}/{part2}" will never goes into "DefaultApi".
Add an named action to your "DefaultApi" to tell the route engine which action to enter. Otherwise once you have more than one actions in your controller, the engine won't know which one to use and throws "Multiple actions were found that match the request: ...". Then to make it matches your Get method, use an ActionNameAttribute.
So your route should like this:
// Map this rule first
RouteTable.Routes.MapRoute(
"WithActionApi",
"api/{controller}/{action}/{id}"
);
RouteTable.Routes.MapRoute(
"DefaultApi",
"api/{controller}/{id}",
new { action="DefaultAction", id = System.Web.Http.RouteParameter.Optional }
);
And your controller:
[ActionName("DefaultAction")] //Map Action and you can name your method with any text
public string Get(int id)
{
return "object of id id";
}
[HttpGet]
public IEnumerable<string> ByCategoryId(int id)
{
return new string[] { "byCategory1", "byCategory2" };
}
You can solve your problem with help of Attribute routing
Controller
[Route("api/category/{categoryId}")]
public IEnumerable<Order> GetCategoryId(int categoryId) { ... }
URI in jquery
api/category/1
Route Configuration
using System.Web.Http;
namespace WebApplication
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
}
}
and your default routing is working as default convention-based routing
Controller
public string Get(int id)
{
return "object of id id";
}
URI in Jquery
/api/records/1
Route Configuration
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Attribute routing.
config.MapHttpAttributeRoutes();
// Convention-based routing.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Review article for more information Attribute routing and onvention-based routing here & this
Try this.
public class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var json = config.Formatters.JsonFormatter;
json.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
config.Formatters.Remove(config.Formatters.XmlFormatter);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional , Action =RouteParameter.Optional }
);
}
}
The possible reason can also be that you have not inherited Controller from ApiController.
Happened with me took a while to understand the same.
To differentiate the routes, try adding a constraint that id must be numeric:
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
constraints: new { id = #"\d+" }, // Only matches if "id" is one or more digits.
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);

Resources