I've been using AttributeRouting for quite some time in my MVC application. However, one thing it always lacked is subdomain routing in Web Api (among other features in that library that work with MVC but not Web Api).
Now I just read about the new improvements in Web Api regarding Attribute Routing and that it's now included with Web Api out of the box.
However, I see no mention of subdomain routing. Is it supported in Web Api 2?
If not, how can I get subdomain routing in my Web Api so that I can hit the ApiController using http://api.mydomain.com/cars/1?
Routing is normally used for the portion of the URL after the domain/port. As long as you have your host configured to let Web API handle requests for a domain, you should be able to route URLs within that domain.
If you do want routing to be domain-specific (such as only have requests to the api.mydomain.com domain handled by a certain route), you can use a custom route constraint. To do that with attribute routing, I think you'd need to have:
First, The custom route constraint class itself. See http://www.asp.net/mvc/tutorials/controllers-and-routing/creating-a-custom-route-constraint-cs for an MVC domain example; the Web API interface is slightly different (http://msdn.microsoft.com/en-us/library/system.web.http.routing.ihttprouteconstraint(v=vs.108).aspx).
Second, A custom route builder. Derive from HttpRouteBuilder and override the BuildHttpRoute method to add your constraint. Something like this:
public class DomainHttpRouteBuilder : HttpRouteBuilder
{
private readonly string _domain;
public DomainHttpRouteBuilder(string domain) { _domain = domain; }
public override IHttpRoute BuildHttpRoute(string routeTemplate, IEnumerable<HttpMethod> httpMethods, string controllerName, string actionName)
{
IHttpRoute route = base.BuildHttpRoute(routeTemplate, httpMethods, controllerName, actionName);
route.Constraints.Add("Domain", new DomainConstraint(_domain));
return route;
}
}
Third, When mapping attribute routes, use your custom route builder (call the overload that takes a route builder):
config.MapHttpAttributeRoutes(new DomainHttpRouteBuilder("api.mydomain.com"));
Related
We have a mvc project and we would like to move to a web api. The mvc project doesn't have any UI,so it was a mistake to use mvc controller other than a web api controller.
However, our customers access the mvc controller via url such as:
https://mysite.azurewebsites.net/indexes/unit/docs/search (both httppost)
https://mysite.azurewebsites.net/indexes/unit/docs/post (both httppost)
https://mysite.azurewebsites.net/indexes/unit/docs/get
the part unit/docs is actually dynamic (something we send to a database to do some query).
If we move this controller to web api, how can we handle the route as we need to be backward compatible?
in your WebApi controller, do the following:
[route("{controller}")]
public IndexesController : ControllerBase
{
[HttpPost("{unit}/{docs}/{search}")]//or whatever those variables should be
public async Task<IactionResult> Search(string unit, string docs, string search)
{
//Do your stuff
var result = _logic.DoStuff();
return Ok(result);
}
}
There's no reason to move away from MVC controller though. Just always return a JsonResult() and it'll behave exactly like a WebApi from the caller's perspective.
I want to be able to identify a resource by different id types. for example:
GET http://example.com/customers/internalId=34 to go to
public Customer GetByInternalId(int internalId){...}
and
GET http://example.com/customers/externalId='JohnDoe' to go to
public Customer GetByExternalId(string externalId){...}
I know I can do this by having some parsing logic in a generic controller method but I don't want to do that. How do I achieve this using the routing feature of asp.net webapi if that is possible.
I would suggest that you try and avoid doing what you are suggesting. Creating two distinct URIs for the same resource will make it harder to use caching. Instead I would suggest using one URL to redirect to the other.
e.g.
> GET /customers/34
< 200 OK
> GET /Customers?name=JohnDoe
< 303 See Other
< Location: http://example.com/customers/34
Your methods do not make much of a sense, why would you return void from a method that begins with Get....?
Also, these routes:
http://example.com/customers/internalId=34
http://example.com/customers/externalId='JohnDoe
Are invalid from MVC/Web API perspective. This is how they should look like:
http://example.com/customers?internalId=34
http://example.com/customers?externalId=John
Default Web API routing should differentiate between the two and route it to different actions.
EDIT:
Create action with the following template:
[HttpGet]
public string InternalId(int id)
{
return id.ToString();
}
Define route for Web Api:
config.Routes.MapHttpRoute(
name: "Weird",
routeTemplate: "{controller}/{action}={id}",
defaults: new { id = RouteParameter.Optional }
);
This allows you to write:
http://localhost:7027/values/internalId=12
Try it...
Then you can just add another method:
[HttpGet]
public string ExternalId(string id)
{
return id;
}
And this:
http://localhost:7027/values/externalId=bob
Will work as well.
Clearly name of my controller is ValuesController as I've just tested this with default Web Api template.
How can we make an ASP.NET MVC4 route that uses subdomain information to determine its route? For example:
website1.domain.com goes to domain.com\websites\1
website2.domain.com goes to domain.com\websites\2
This is a dynamic mapping like this: websiteN.domain.com goes to domain.com\websites\N
I have a username parameter,How Can I pass through controller/action?
ASP.NET's built-in routing doesn't directly support sub-domain routing. But fortunately, there's AttributeRouting, which is a very popular add-on library for routing that allows you to do lots of fancy routing, including sub-domain routing.
Here's an example from the Attribute Routing site:
[RouteArea("Users", Subdomain = "users")]
public class SubdomainController : Controller
{
[GET("")]
public ActionResult Index() { /* ... */ }
}
I've been playing around with ASP.NET MVC 4 beta and I see two types of controllers now: ApiController and Controller.
I'm little confused at what situations I can choose a particular controller.
For ex: If I want to return a view then I've to use ApiController or the ordinary Controller? I'm aware that the WCF Web API is now integrated with MVC.
Since now we can use both controllers can somebody please point at which situations to go for the corresponding controller.
Use Controller to render your normal views. ApiController action only return data that is serialized and sent to the client.
here is the link
Quote:
Note If you have worked with ASP.NET MVC, then you are already familiar with controllers. They work similarly in Web API, but controllers in Web API derive from the ApiController class instead of Controller class. The first major difference you will notice is that actions on Web API controllers do not return views, they return data.
ApiControllers are specialized in returning data. For example, they take care of transparently serializing the data into the format requested by the client. Also, they follow a different routing scheme by default (as in: mapping URLs to actions), providing a REST-ful API by convention.
You could probably do anything using a Controller instead of an ApiController with the some(?) manual coding. In the end, both controllers build upon the ASP.NET foundation. But having a REST-ful API is such a common requirement today that WebAPI was created to simplify the implementation of a such an API.
It's fairly simple to decide between the two: if you're writing an HTML based web/internet/intranet application - maybe with the occasional AJAX call returning json here and there - stick with MVC/Controller. If you want to provide a data driven/REST-ful interface to a system, go with WebAPI. You can combine both, of course, having an ApiController cater AJAX calls from an MVC page.
To give a real world example: I'm currently working with an ERP system that provides a REST-ful API to its entities. For this API, WebAPI would be a good candidate. At the same time, the ERP system provides a highly AJAX-ified web application that you can use to create queries for the REST-ful API. The web application itself could be implemented as an MVC application, making use of the WebAPI to fetch meta-data etc.
Which would you rather write and maintain?
ASP.NET MVC
public class TweetsController : Controller {
// GET: /Tweets/
[HttpGet]
public ActionResult Index() {
return Json(Twitter.GetTweets(), JsonRequestBehavior.AllowGet);
}
}
ASP.NET Web API
public class TweetsController : ApiController {
// GET: /Api/Tweets/
public List<Tweet> Get() {
return Twitter.GetTweets();
}
}
I love the fact that ASP.NET Core's MVC6 merged the two patterns into one because I often need to support both worlds. While it's true that you can tweak any standard MVC Controller (and/or develop your own ActionResult classes) to act & behave just like an ApiController, it can be very hard to maintain and to test: on top of that, having Controllers methods returning ActionResult mixed with others returning raw/serialized/IHttpActionResult data can be very confusing from a developer perspective, expecially if you're not working alone and need to bring other developers to speed with that hybrid approach.
The best technique I've come so far to minimize that issue in ASP.NET non-Core web applications is to import (and properly configure) the Web API package into the MVC-based Web Application, so I can have the best of both worlds: Controllers for Views, ApiControllers for data.
In order to do that, you need to do the following:
Install the following Web API packages using NuGet: Microsoft.AspNet.WebApi.Core and Microsoft.AspNet.WebApi.WebHost.
Add one or more ApiControllers to your /Controllers/ folder.
Add the following WebApiConfig.cs file to your /App_Config/ folder:
using System.Web.Http;
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Finally, you'll need to register the above class to your Startup class (either Startup.cs or Global.asax.cs, depending if you're using OWIN Startup template or not).
Startup.cs
public void Configuration(IAppBuilder app)
{
// Register Web API routing support before anything else
GlobalConfiguration.Configure(WebApiConfig.Register);
// The rest of your file goes there
// ...
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureAuth(app);
// ...
}
Global.asax.cs
protected void Application_Start()
{
// Register Web API routing support before anything else
GlobalConfiguration.Configure(WebApiConfig.Register);
// The rest of your file goes there
// ...
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// ...
}
This approach - together with its pros and cons - is further explained in this post I wrote on my blog.
Quick n Short Answer
If you want to return a view, you should be in "Controller".
Normal Controller - ASP.NET MVC: you deal with normal "Controller" if you are in ASP.net Web Application. You can create Controller-Actions and you can return Views().
ApiController Controller: you create ApiControllers when you are developing ASP.net REST APIs. you can't return Views (though you can return Json/Data for HTML as string). These apis are considered as backend and their functions are called to return data not the view
More Details here
Every method in Web API will return data (JSON) without serialization.
However, in order to return JSON Data in MVC controllers, we will set the returned Action Result type to JsonResult and call the Json method on our object to ensure it is packaged in JSON.
The main difference is: Web API is a service for any client, any devices, and MVC Controller only serve its client. The same because it is MVC platform.
If you create a new web application in latest framework 4.7.2 you will both of them to be configured by default and can build you application on that
In Asp.net Core 3+ Vesrion
Controller: If wants to return anything related to IActionResult & Data also, go for Controllercontroller
ApiController: Used as attribute/notation in API controller. That inherits ControllerBase Class
ControllerBase: If wants to return data only go for ControllerBase class
I’m working out the concepts for a new project where I need to support for multilingual URL’s. Ideally all URL’s need to be in the native language of the user. So we don’t want to use domain.com/en/contact and domain.com/es/contact but we like domain.com/contact and domain.com/contactar (contactar is Spanish for contact). Internally both should be routed to the same ContactController class.
This could be handled by adding multiple static routes to Global.asax.cs for each language but we’d like to make this very dynamic and would like the user of the system to be able to change the translation of the URL’s through the content management system. So we need some kind of dynamic mapping from URL’s to controllers and actions.
By looking at the source code of MVC3 I figured out that the ProcessRequestInit method of MvcHandler is responsible for determining which controller to create. It simply looks in the RouteData to get the name of the controller. One way to override the default MVC routing would be to create a simple default route that uses a custom RouteHandler. This RouteHandler forces MVC to use my own custom subclassed version of MvcHandler that overrides the ProcessRequestInit method. This overridden method insert my own dynamically found controller and action into the RouteData before calling back to the original ProcessRequestInit.
I’ve tried this:
Global.asax.cs
routes.Add(
new Route("{*url}", new MultilingualRouteHandler())
{
Defaults = new RouteValueDictionary(new { controller = "Default", action = "Default" })
}
);
MultilingualRouteHandler.cs
public class MultilingualRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MultilingualMVCHandler(requestContext);
}
}
MultilingualMvcHandler.cs
public class MultilingualMVCHandler : MvcHandler
{
public MultilingualMVCHandler(RequestContext context) : base(context)
{
}
protected override void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
if (RequestContext.RouteData.Values.ContainsKey("controller"))
{
RequestContext.RouteData.Values.Remove("controller");
}
if (RequestContext.RouteData.Values.ContainsKey("action"))
{
RequestContext.RouteData.Values.Remove("action");
}
RequestContext.RouteData.Values.Add("controller", "Product");
RequestContext.RouteData.Values.Add("action", "Index");
base.ProcessRequestInit(httpContext, out controller, out factory);
}
}
In this handler I hardcoded the controller and action for testing purposes to some fixed values but it’s not difficult to make this dynamic. It works but the only problem is that I had to modify the source code of ASP.NET MVC3 to get it working. The problem is that the ProcessRequestInit method of MvcHandler is private and thus cannot be overridden. I’ve modified the source code and changed it to protected virtual which allows me to override it.
This is all great but possibly not the best solution. It’s cumbersome that I would always need to distribute my own version of System.Web.Mvc.dll. It would be much better that it would work with the RTM version.
Am I missing any other possibilities of hooking into ASP.NET MVC that would allow me to dynamically determine the controller and action to launch, depending on the URL? One other way I thought of is to build the RouteCollection dynamically on *Application_Start* but I think that will make it more difficult to change it on the fly.
I would appreciate any tips of hooks that I’ve not yet found.
This is fairly old now, nut just in case anyone else is looking for something similar...
Unless I'm completely misunderstanding what you want to do, it's pretty simple really.
Step 1: Add a new route to global.ascx.cs containing a reference to your personal routing engine
routes.Add(new MyProject.Routing.ContentRoutingEngine());
Make sure that it is in the right place in the list of routes so that other routing engines can catch stuff before it if required, or continue the route search if your engine doesn't handle a particular route. I put it after the ignores, but before the MVC default routes.
Step 2: Create the Content Routing Engine, making sure that it inherites from System.Web.Routing.RouteBase abstract class, and overrides the GetRouteData and GetVirtualPath methods as required e.g.
public class ContentRoutingEngine : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeHandler = new MvcRouteHandler();
var currentRoute = new Route("{controller}/{action}", routeHandler);
var routeData = new RouteData(currentRoute, routeHandler);
// set your values dynamically here
routeData.Values["controller"] = "Home" ;
// or
routeData.Values.Add("action", "Index");
// return the route, or null to have it passed to the next routing engine in the list
return routeData;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//implement this to return url's for routes, or null to just pass it on
return null;
}
}
and that should do it. You can change routes as dynamically as you wish within your engine, and no changes to MVC source required. Let the standard MVC RouteHandler actually invoke the controller.
Postscript: Obviously the code above is not production standard - it's written to make it as obvious as possible what's going on.
If you are allowing modification of urls through your CMS, then you will have to keep all old versions of the urls so that you can 301 redirect to the new ones.
The best bet for this will be to put the url tokens eg "contactar" in the db along with its corresponding controller.
query that, and create your routes out of that.
create a route that will handle the 301s
I think that most elegant solution would be using some action filter combined with custom ActionInvoker. That way, you could invoke an action that has specific filters applied. Something like ActionName attribute, only capable to accept multiple values (names).
Edit: Take a look at ActionMethodSelectorAttribute, meybe you don't need a custom ActionInvoker after all.