asp.net WEB API: Routing - Action before Controller. Possible workaround? - asp.net

I am developping a WEB API using asp.net and my goal is to be able to call this type of url's:
/html/countries/...
/json/countries/...
Countries is a controller, and depending on the parameter before it returns different result.
What i did and it doesn't seems to work:
routes.MapRoute(
name: "Default",
url: "api/{action}/{controller}",
defaults: new
{
}
);
CountriesController:
[ActionName("html")]
public string get()
{
//...
}
[ActionName("json")]
public void getType()
{
//...
}
Any sugestions?
EDIT:
I have like 7 controllers.
And there are some possible urls:
/html/{controller}/x/y
/json/{controller}/x/y/order/h
/html/{controller}/x/z/order/y/j
/json/{controller}/x/z/order/y/j

Allow me by start saying that if "html" or json action means "format" those should not be part of your controller they are media types and needs to be configured differently
Web Api v1 defines resources globally in the global.asax on application_start event. Assuming you are using Visual Studio 2013 and based Microsoft default template your method may look like this:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
WebApi routing configuration occurs here WebApiConfig.Register while your MVC configuration occurs here RouteConfig.RegisterRoutes
Your WebApi routing configuration should look like this
public static class WebApiConfig{
public static void Register(HttpConfiguration config){
config.Routes.MapHttpRoute(
name: "htmltApi",
routeTemplate: "html/{action}/{controller}",
);
config.Routes.MapHttpRoute(
name: "jsonApi",
routeTemplate: "json/{action}/{controller}",
);
...
Another important detail is that WebApi v2 introduced something called Route Attributes those can be used along with your Controller class and can facilitate the routing configuration.
For example:
public class BookController : ApiController{
//where author is a letter(a-Z) with a minimum of 5 character and 10 max.
[Route("html/{id}/{newAuthor:alpha:length(5,10)}")]
public Book Get(int id, string newAuthor){
return new Book() { Title = "SQL Server 2012 id= " + id, Author = "Adrian & " + newAuthor };
}
[Route("json/{id}/{newAuthor:alpha:length(5,10)}/{title}")]
public Book Get(int id, string newAuthor, string title){
return new Book() { Title = "SQL Server 2012 id= " + id, Author = "Adrian & " + newAuthor };
}
...

Thanks to the Dalorzo answer i did find the problem:
The problem occurred because my application was created the following way:
which resulted in creation of both files, RouteConfig.cs (MVC) and WebApiConfig.cs (WEB API):
What is WRONG is that the code in the question is from RouteConfig.cs
After putting the code
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "api/{action}/{controller}",
defaults: new
{
action = "html"
}
);
in WebApiConfig.cs, it worked proper way.

Related

ASP.NET ApiController inside a webform can't reach methods

I can't reach any methods from my ApiController in anyway, the routing does appear if i try to reach it by a browser but no methods are shown.
My Controller:
namespace AgroRiego.Controllers
{
public class datacontrol : ApiController
{
[HttpGet, Route("api/get")]
public string Get([FromUri]string user, string pass)
{
string check = SQL.Reader("SELECT * FROM users WHERE username='" + user + "' AND password='" + pass + "'");
if (String.IsNullOrWhiteSpace(check))
{
return "error en credenciales";
}
DataTable horarios = SQL.table_read("SELECT * FROM horario_riego");
string json = Utils.ConvertDataTabletoJSON(horarios);
return json;
}
[HttpPost, Route("api/post")]
public void Post([FromBody]string value)
{
string i = value;
}
}
}
my global asax:
namespace AgroRiego
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
and my webapiconfig:
namespace AgroRiego
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ConfiguraciĆ³n y servicios de API web
// Rutas de API web
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
i have more webforms inside the project (originally it was just html pages with serverside code, but i need to add a couple methods to retrieve and send data, help much appreciated!
EDIT1: i managed to reach HTTP 200 changing the URL but i can't reach the methods anyway (in debug mode it does not stop on the breakpoints) how can i route correctly the Api (so it is not Login.aspx) and how do i fix the methods reaching?
EDIT2: i read in documentation that i need this line in global:
RouteConfig.RegisterRoutes(RouteTable.Routes);
but im not using MVC does that matter? i tried reaching the routes with a brand new MVC Web Api and it yields "No Response"
use a routerprefix with your controller. So you access the URL as
http://localhost/routerprefix/router
HttpClient class can be use to send and receive HTTP requests and responses. Since you are trying to consume a WebApi from a aspx page, better way is to create a HttpClient instance
Below is a very simple implementation. Please check this url for further information
MSDN sample
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://localhost:49342/api/get");
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync();
}
By the look of your set up, it seems correct
you have got:
config.MapHttpAttributeRoutes(); - setup the attribute route
config.Routes.MapHttpRoute( - setup the default route
GlobalConfiguration.Configure(WebApiConfig.Register); - to register at startup
so it should work.
I think the problem you are having is the way you are calling it
WebAPI routing work a little different to MVC
for example:
In you get method, the route is set as below
[HttpGet, Route("api/get")]
so you should call it {host}/api/get using a GET http method
in the screen shot, you are calling using {host}/api/get/Get - which would not have work, because no route would match
Same for your POST method
So give it another try and you should be able to reach it
The URL to add in the rest testing tool is
http://localhost:49342/api/get
Method type is GET
If you are calling this web api from aspx page use the httpClient class.

AttributeRouting with IHttpControllerSelector - Api Versioning

I am trying to achieve api versioning using a CustomHttpControlSelector and AttributeRouting on asp.net webapi.
What i am trying to do is distinguish controller's versions by it's namespaces.
if a request is made to /api/v2/foo/bar
i want it to match
namespace Web.Controllers.Api.v2
{
[RoutePrefix("foo")]
public class LongerThanFooController : ApiController
{
[HttpGet]
[Route("bar")]
public string BarFunction()
{
return "foobar";
}
}
}
but as i see when i don't use full url on RoutePrefix (/api/v2/foo) attribute routing doesn't kick in and i get null when i call
request.GetRouteData().GetSubRoutes();
on my CustomHttpControlSelector. i don't want to Repeat /api/v2 on every controller.
if i decide to remove attributeRouting and use manual routes like
config.Routes.MapHttpRoute(
name: "DefaultVersionedApi",
routeTemplate: "api/v{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, version = Config.LatestVersion }
);
i lose all flexibility of naming my controllers and functions.
is there a way to get out of this limbo?
note: for the CustomHttpControlSelector i modified code on http://aspnet.codeplex.com/SourceControl/changeset/view/dd207952fa86#Samples/WebApi/NamespaceControllerSelector/NamespaceHttpControllerSelector.cs
I realize this is bit of an old question now, but it can answered using the ASP.NET API Versioning package for ASP.NET Web API. In the latest 3.0 version, you can achieve your scenario by updating your configuration with:
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
configuration.AddApiVersioning(
options =>
{
options.Conventions.Add( new VersionByNamespaceConvention() );
options.AssumeDefaultVersionWhenUnspecified = true;
options.ApiVersionSelector = new CurrentImplementationApiVersionSelector( options );
} );
configuration.MapHttpAttributeRoutes( constraintResolver );
You should also remove your convention-based routes. Those are unnecessary if you are using attribute routing.
The setup for your controller simply changes to:
namespace Web.Controllers.Api.v2
{
[RoutePrefix("api")]
public class LongerThanFooController : ApiController
{
[HttpGet]
[Route("foo/bar")]
[Route("v{version:apiVersion}/foo/bar")]
public string BarFunction()
{
return "foobar";
}
}
}
The reason you need two route definitions is that you cannot have default values in the middle of the route template. Default values can only be used at the end. This also means that you need to allow no API version to be specified and indicate the way to determine which API version should be selected is to use the current implementation (e.g. latest). I'm personally not a fan of this approach because I think things should be predictable to clients, but this will achieve your desired result.

Angularjs cant find view, tried everything I know

I have made a project using ng.Net template from visual studio, i got it up and running, i added a new ProgramController.cs, Program.cshtml template, programCtrl.js angular controller, and a program angular module.
And it just will not work.
I have a ASP.NET web api and angularjs on clientside.
Here are 2 example routes:
$routeProvider.when('/todomanager', {
templateUrl: 'App/TodoManager',
controller: 'todoManagerCtrl'
});
$routeProvider.when('/program', {
templateUrl: 'App/Program',
controller: 'programCtrl'
});
And what they do backend:
[HttpGet]
[Authorize]
public List<Program> GetPrograms()
{
string userId = Request.GetOwinContext().Authentication.User.Identity.GetUserId();
var currentUser = UserManager.FindById(userId);
return currentUser.Programs;
//return db.Programs;
}
[HttpGet]
[Authorize]
public List<todoItem> GetUserTodoItems()
{
string userId = Request.GetOwinContext().Authentication.User.Identity.GetUserId();
var currentUser = UserManager.FindById(userId);
return currentUser.todoItems;
}
The first one works, the second one gives tpload compile error (could not find template view)
I can get the TodoManager view if I call it in /program
But I cant reach my program.cshtml. It's in the same folder as TodoManager.cshtml
I could provide more code, But I dont know which, Because it all works.
My closest guess so far, is that I dont have access to that view, I'm being blocked by a blockviewhandler or the view doesnt exist.
I have the access.
If it was the viewblockhandler it would also block TodoManager.cshtml
And it exists xD I'm looking at it..
It's like i'm spamming 4 + 4 on a calculator and it keeps returning 5..
Please, anything, been stuck for 2 workdays.
EDIT additional code
//RouteConfig RegisterRoutes
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "App",
url: "{url}",
defaults: new { controller = "Main", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Main", action = "Index", id = UrlParameter.Optional }
);
}
//WebApiConfig Register
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();
//I chaned the routeTemplate so that methods/services would be identified by their action, and not by their parameters.
//I was getting conflicts if I had more than one GET services, that had identical parameter options, but totally different return data.
//Adding the action to the routeTemplte correct this issue.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}", //routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
//blockviewhandler in Web.config
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*." verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
Edit: Project structure, i have a HomeController that returns my Index.cshtml in there, i have my ng-view which is where my views should be rendered.
Many seem to misunderstand my real question, i want to know:
How is it, that the todoManager works, and the program does not.
When using ASP.NET MVC, Web API etc. you should add "every" view to the mvc controller, you probably have something like this in one of your controllers:
public ActionResult TodoManager()
{
return PartialView();
}
assuming you are following a tutorial or modifying a sample it is probably in Controllers/AppController. If true you must add your another view as:
public ActionResult Program()
{
return PartialView();
}

Can you use the attribute-based routing of WebApi 2 with WebForms?

As the title states, I'm wondering if you can use the attribute-based routing of WebAPI 2 with WebForms. I feel like this can obviously be done given you can use WebAPI2 just fine in a WebForms application... I just can't figure out how to enable attribute-based routing.
Based on this article, I understand you normally enable it via a call to MapHttpAttributeRoutes() prior to setting up your convention-based routes. But I'm guessing this is the MVC way - I need to know the equivalent for WebForms.
I currently use MapHttpRoute() to set up my convention-based routes, and I'd like to try out the attribute-based routing in WebAPI2. I have updated my project with WebAPI2 - I just need to know how to enable the attribute-based routing feature.
Any info would be appreciated.
You need not do anything special in case of WebForms. Web API attribute routing should work just as in MVC.
If you are using VS 2013, you can test this easily by create a project using "Web Forms" template and then choose "Web API" check box and you should see all the following code generated by this.
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 }
);
}
}
Global.asax
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
WebForm's RouteConfig
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings);
}
}

Pluralsight 404 not found error

I have a question about MVC3 in the Pluralsight examples. I'm new to MVC and I have what will appear to be a simple question. I downloaded the sample code and added the Routemap to global.asax.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace OdeToFood
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Cuisine",
"cuisine/{name}",
new { controller = "cuisine", action = "Search" }
);
/* routes.MapRoute(
"Cuisine",
"{controller}/{name}",
new { controller = "cuisine", action = "Search" }
); */
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
And added the Controller:
namespace OdeToFood.Controllers
{
public class CuisineController : Controller
{
//
// GET: /Cuisine/
public ActionResult Search()
{
return Content("You have reached the Cuisine controller");
}
}
}
As shown in the tutorial "Controller Action Parameter!" and run the application with the word cuisine (all spelled correctly - even changing to all capitalization as a test) and I still get the HTTP 404 "not found error".
I'm running on Windows 7 with VS 2012 and .net 4.5 installed (this is a new box and may not have ever had previous versions. MVC 3 and MVC 4 are in the new project selection so those must be isntalled correclty.
Any ideas on what I'm doing wrong? Did I miss a step? I see that IIS6 or IIS7 might/must be on the machine? I have come to believe that IIS doesn't run on windows 7. Is that true? Do I require iis? The sample code works fine until this change...
I'm a little over my head here as I learn this new stuff. Thank you for your patience and help!
Try using:
routes.MapRoute(
"Cuisine",
"cuisine/{name}",
new { controller = "cuisine", action = "Search", name = "" }
);
Try using the a MVC Route Debugger so that you can visually see which routes are being matched. The one I use is Phil Haack's, but there are others available:
Install-Package RouteDebugger
Separately, don't you need a name parameter on your search action to match this route?

Resources