WebAPI route - A better route - asp.net-mvc-routing

Currently this works:
/api/Company/1089?children=branches
Controller:
public IEnumerable<Branch> Get(int id, string children)
I want my url to be this:
/api/Company/1089/branches
I can't figure out how to configure the route.
This doesn't work:
routes.MapRoute(
name: "cb",
url: "{controller}/{action}/{id}/{children}",
defaults: new { controller = "Company", action = "Get",
id = UrlParameter.Optional, children = UrlParameter.Optional }
);

api/{controller}/{id}/{children}

Related

Response.RedirectToRoute(RouteData.Values) redirects to the Area controller

I am trying to setup a BaseController to handle culture as part of the url (based on ASP.NET MVC 5 Internationalization). My implementation works properly as long as I disable my Areas' registration.
When One of my Area is registered, if I try to input a wrong/not supported culture (http://localhost:52639/zz/), I experience a 404 error with a request URL: http://localhost:52639/fr/Test/Post.
I have checked my routes are properly registered.
If I do the same while disabling the Areas registration, the base controller and routing behave correctly if I type the following URL: http://localhost:52639/zz/ I am redirected to http://localhost:52639/fr/ (default culture).
Those are my routes:
public static void RegisterRoutes(RouteCollection routes)
{
var namespaces = new[]{typeof(PostController).Namespace};
routes.IgnoreRoute("favicon.ico");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("PostToHack", "{culture}/Post/{idAndSlug}", new { culture = "", Controller = "Post", Action = "Show" }, namespaces);
routes.MapRoute("Post", "{culture}/Post/{id}-{slug}", new { culture = "", Controller = "Post", Action = "Show" }, namespaces);
routes.MapRoute("TagToHack", "{culture}/Tag/{idAndSlug}", new { culture = "", Controller = "Post", Action = "Tag" }, namespaces);
routes.MapRoute("Tag", "{culture}/Tag/{id}-{slug}", new { culture = "", Controller = "Post", Action = "Tag" }, namespaces);
routes.MapRoute("Logout", "{culture}/Logout", new { culture = "", Controller = "Authentication", Action = "Logout" }, namespaces);
routes.MapRoute("Login", "{culture}/Login", new { culture = "", Controller = "Authentication", Action = "Login" }, namespaces);
//Error routes
routes.MapRoute("Error404", "{culture}/errors/404", new { culture = "", Controller = "Errors", Action = "NotFound" }, namespaces);
routes.MapRoute("Error500", "{culture}/errors/500", new { culture = "", Controller = "Errors", Action = "Error" }, namespaces);
routes.MapRoute("Home", "{culture}", new { culture = "", Controller = "Post", Action = "Index"},namespaces);
//Never to be called by user which is why it comes after MapRoute Home so it is always overwritten by it
routes.MapRoute("Sidebar", "{culture}", new { culture = "", Controller = "Layout", Action = "Sidebar"},namespaces);//This is a "child-only" controller
routes.MapRoute("NavigationBar", "{culture}", new { culture = "", Controller = "Layout", Action = "NavigationBar"},namespaces);//This is a "child-only" controller
Area Route
public override void RegisterArea(AreaRegistrationContext context)
{
var namespaces = new[] { typeof(PostsController).Namespace };
context.MapRoute(
"admin_default",
"{culture}/admin/{controller}/{action}/{id}",
new { culture = "", action = "Index", id = UrlParameter.Optional }, namespaces
);
}
Base Controller:
public class BaseController : Controller
{
protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
{
var cultureName = RouteData.Values["culture"] as string;
// Attempt to read the culture cookie from Request
if (cultureName == null)
cultureName = (Request.UserLanguages != null) && (Request.UserLanguages.Length > 0)
? Request.UserLanguages[0]
: null; // obtain it from HTTP header AcceptLanguages
// Validate culture name
cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe
if (RouteData.Values["culture"] as string != cultureName)
{
// Force a valid culture in the URL
RouteData.Values["culture"] = cultureName.ToLowerInvariant(); // lower case too
// Redirect user
Response.RedirectToRoute(RouteData.Values);
}
// Modify current thread's cultures
Thread.CurrentThread.CurrentCulture = new CultureInfo(cultureName);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
return base.BeginExecuteCore(callback, state);
}
}
After some more digging I have found a solution that work for me. My issue was coming from the order in which I was registering my routes. I was registering my Area's routes first:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
I inverted the order and made sure that I was only registering my Area's routes after:
protected void Application_Start()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
AreaRegistration.RegisterAllAreas();
}

how to handle query string parameter in asp.net web api

here is how I have routing setup.
routes.MapHttpRoute(
name: "Authors",
routeTemplate: "api/authors",
defaults: new { controller = "authors" }
);
controller action method
// GET /api/authors/
public string GetAuthors(string author_ids)
{
return data;
}
Url http://site.com/api/authors?author_ids=1 actually calls controller action but when I don't pass querystring parameter, it says no controller action matching found.
How to handle optional querystring parameter when defining route?
// GET /api/authors/
public IEnumerable<string> GetAuthors()
{
return data;
}
You will need to define an action that takes no parameters.
It would be better, however, to add id to your route as optional:
routes.MapHttpRoute(
name: "Authors",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You can be specific like:
routes.MapHttpRoute(
name: "Authors",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and Action can be:
// GET /api/authors/?XXXX
public IEnumerable<string> GetAuthors( [FromUri] String author_ids)
{
return data;
}
This will only match query string . [FromUri] lets the parameter to be taken from query string.
You may specific like:
routes.MapHttpRoute(
name: "Authors",
routeTemplate: "api/{controller}/{author_ids}",
defaults: new { author_ids = RouteParameter.Optional }
);

ASP.NET MVC 3 - Custom SEO friendly routes

I've defined the following route:
routes.MapRoute(
null,
"foo/{id}/{title}",
new { controller = "Boo", action = "Details" }
);
When I call this method:
Url.Action("Details", "Boo", new { id = article.Id, title = article.Title })
I get the following URL:
http://localhost:57553/foo/1/Some%20text%20Š
I would like to create a new route that will lowercase all characters and replace some of them.
e.g.
http://localhost:57553/foo/1/some-text-s
Rules:
Uppercase -> lowercase
' ' -> '-'
'Š' -> 's'
etc.
Any help would be greatly appreciated!
Seems like a perfect candidate for a custom route:
public class MyRoute : Route
{
public MyRoute(string url, object defaultValues)
: base(url, new RouteValueDictionary(defaultValues), new MvcRouteHandler())
{
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
values = new RouteValueDictionary(values);
var title = values["title"] as string;
if (!string.IsNullOrEmpty(title))
{
values["title"] = SEOify(title);
}
return base.GetVirtualPath(requestContext, values);
}
private string SEOify(string title)
{
throw new NotImplementedException();
}
}
which will be registered like this:
routes.Add(
"myRoute",
new MyRoute(
"foo/{id}/{title}",
new { controller = "Boo", action = "Details" }
)
);
Now all you have to do is to implement your SEO requirements in the SEOify function that I left. By the way you could get some inspiration from the way StackOverflow does it for the question titles.

asp mvc 404 when + in url

I have problem with asp mvc 3 application. When I put the + character in a url I always get a 404 error. All requests are ajax get request.
If I make this request Test/Details/+ I get 404: Test/Details/+
This is request in fiddler: GET /Test%2FDetails%2F%2B?t=1318678807718 HTTP/1.1
Here are routes.
routes.MapRoute(
"PagingTwoTest", // Route name
"{controller}/{action}/{tag}/p{currentPage}/p{secCurrentPage}/{*term}", // URL with parameters
new { secCurrentPage = UrlParameter.Optional, term = UrlParameter.Optional }, // Parameter defaults
new { currentPage = "\\d+", secCurrentPage = "\\d+" }
);
routes.MapRoute(
"PagingTwo", // Route name
"{controller}/{action}/p{currentPage}/p{secCurrentPage}/{*term}", // URL with parameters
new { secCurrentPage = UrlParameter.Optional, term = UrlParameter.Optional }, // Parameter defaults
new { currentPage = "\\d+", secCurrentPage = "\\d+" }
);
routes.MapRoute(
"Paging", // Route name
"{controller}/{action}/p{currentPage}/{*term}", // URL with parameters
new { term = UrlParameter.Optional }, // Parameter defaults
new { currentPage = "\\d+" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"DefaultName", // Route name
"{controller}/{action}/{*id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
In your constraints you are not considering when someone enters a string you allways request a digit, so I think you can try with:
routes.MapRoute(
"TestDetails", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {
controller = "Test",
action = "Details",
id = UrlParameter.Optional
}
);
So in your TestController you can request a string:
public class TestController : Controller
{
....
....
public ActionResult Details(string? id) //So you can verify if is null
{
ViewData["variable"] = id;
return View();
}
....
....
}
And in your Details.aspx you can put something like this:
<%# Page Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Title, what you want
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<p> Blah, blah, blah, you request this: <%: ViewData["variable"] %> </p>
</asp:Content>

Querystring Route in MVC2

I'm trying to create a route to a specific controller/action which needs to accept optional querystring parameters.
the urls i'd like to accept are:
/Products/ProductsListJson
/Products/ProductsListJson?productTypeId=1
/Products/ProductsListJson?productTypeId=1&brandId=2
/Products/ProductsListJson?productTypeId=1&brandId=2&year=2010
I have an action like this:
public JsonResult ProductsListJson(int productTypeId, int brandId, int year)
And a route like this:
routes.MapRoute(
null, "Products/ProductsListJson",
new { controller = "Products", action = "ProductsListJson", productTypeId = 0, brandId = 0, year = 0 }
);
I assumed that the action "ProductsListJson" would simply see the querystring urls and map them to the appropriate arguments however this is not happening.
Anyone know how this could be achived?
You don't need to specify their values in the route if those parameters are passed in the query string:
routes.MapRoute(
null, "Products/ProductsListJson",
new { controller = "Products", action = "ProductsListJson" }
);
and your action:
public ActionResult ProductsListJson(int? productTypeId, int? brandId, int? year)
{
...
}
but you probably don't need a specific route for this as the default route will handle it just fine:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);