How to catch-all URLs with MapHttpRoute - asp.net-mvc

In addition to all the defined api routes, I want to handle any other url in a LegacyUriRedirect action.
I have tried to leave the routeTemplate empty, but it catches only the root url and ignores any urls with segments.
How do I change the below code so that it catches all urls with any segments?
config.Routes.MapHttpRoute(
name: "LegacyUriRedirect",
routeTemplate: "",
defaults: new { controller = "URI", action = "LegacyUriRedirect" }
);

config.Routes.MapHttpRoute(
name: "LegacyUriRedirect",
routeTemplate: "{*catchall}",
defaults: new { controller = "URI", action = "LegacyUriRedirect" }
);

Related

Dealing with multiple GET actions Web API

I am new to WEB API and trying to set up routing for multiple GET actions.
Controller Code
// Get api/values
public IEnumerable<tblUser> Get()
{
//whatever
}
// Get api/values/action
[ActionName("GetByQue")]
public IEnumerable<tblQue> GetQue()
{
//whatever
}
// Get api/values/action
[ActionName("GetUserScore")]
public IEnumerable<tblScore> GetScore(string user)
{
//whatever
}
Config
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional}
);
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetByQue" }
);
config.Routes.MapHttpRoute(
name: "DefaultStringApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetUserScore" }
);
When I try with http://localhost:54118/api/remote/GetByQue URL getting this error
{
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.String Get(Int32)' in 'HydTechiesApi.Controllers.HydTechiesApiController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
is my routing wrong? Any help would be valuable as I am not able to find a solution.
You should add {action} to routeTemplate instead of {id} in the second configuration
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "GetByQue" }
);
also you can try to use route attribure on action :
[ActionName("GetByQue")]
[Route("api/remote/GetByQue")]
public IEnumerable<tblQue> GetQue()
{
//whatever
}
or change order(the second configuration and first configuration) of configuration in WebApiConfig.cs
You made a couple of mistakes in your route. As your example code below:
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetByQue" });
//url: http://localhost:54118/api/remote/GetByQue
(1). routeTemplate: "api/{controller}/{id}". you specify your route has an id and it is not Optional. So your URL must have id. That's what your error showed.you can handle with the issue like below:
defaults: new { id = RouteParameter.Optional }
(2). defaults: new { action = "GetByQue" }); you did not say any thing about action in your routeTemplate. your defaults about action, which does not have any meaning.
(3). From your route, your URL should look like http://localhost:54118/api/remote/5 , you should not get mutiple get method by your route.
There are some solutions, which you may use:
(1). change route like below:
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
please add [HttpGet] in each method just in cause.
[HttpGet]
public IEnumerable<tblUser> Get()
{......}
[HttpGet]
[ActionName("GetByQue")]
public IEnumerable<tblQue> GetQue()
{......}
[HttpGet]
[ActionName("GetUserScore")]
public IEnumerable<tblScore> GetScore(string user)
{......}
Now you can use URL like http://localhost:54118/api/remote/GetByQue
Very Useful Tips: Using [Route("")] tag to specify parameters
**You also have to change Route like above (1)
in Controller, please specify request method to make sure the get method
[HttpGet]
[ActionName("GetUserScore")]
[Route("api/remote/GetScore/{user}")]
public IEnumerable<tblScore> GetScore(string user)
{.....}
//URL: http://localhost:54118/api/remote/GetScore/user

Telerik reports without trdx files

I am banging my head against the brick to get this working. I have reports which are generated using report designer and they are working fine as i have loaded them using an iframe now i want to use Html5 report viewer. But in most of the tutorials out there they are using trdx reports rather than one being generated using report Designer.
I have a class library named XYZ.TelerikReports where all the reportname.cs (reports ) files resides and i have my main project in the same solution where i want to show the reports.
$("#reportViewer1")
.telerik_ReportViewer({
serviceUrl: "/api/reports/",
templateUrl: '/ReportViewer/templates/telerikReportViewerTemplate.html',
reportSource: {
report: "XYZ.TelerikReports.IncomeStatementReport,XYZ.TelerikReports"
parameters: { ReportDataID: parseInt('#state.CurrentReportDataID') }
},
scale: "1.0"
});
Now i am wondering what should be my serviceUrl ?
The serviceUrl expects to be routed to the Telerik Reporting Web API controller. The current value you use /api/reports/ is Telerik's default name for this controller. The documentation details how to implement the Web API controller pretty well. You'll find that here http://www.telerik.com/help/reporting/telerik-reporting-rest-host-http-service-using-web-hosting.html and here http://www.telerik.com/help/reporting/telerik-reporting-rest-implementing-http-service.html.
This controller will resolve the requested report successfully if the report name is that of a report class or a .trdx. Alternatively you can implement a customer report resolver. This can be done by following the instructions in the documentation here http://www.telerik.com/help/reporting/telerik-reporting-rest-custom-report-resolver.html.
So your serviceUrl is fine. You just need to make sure that you have the service it calls setup.
One possible Approach is to modify your reporting API controller to use the report type resolver and alter the CreateReportResolver e.g.
protected override IReportResolver CreateReportResolver()
{
var reportsPath = HttpContext.Current.Server.MapPath("~/Reports");
return new ReportTypeResolver()
.AddFallbackResolver(new ReportFileResolver(reportsPath));
}
Then specify the Fully Qualified Assembly name or classname of each report in the HTML5 Viewer Configuration (as you are already doing).
With regards to the path for the service url, you can use the api/reports route you have above, but you must call the telerik reporting route registration function in your WebApiConfig.Register function, i.e.:
ReportsControllerConfiguration.RegisterRoutes(GlobalConfiguration.Configuration);
This means your reporting api is inside a controller called ReportsController.
Alternatively, you can customize the path to something like /Controllers/MyCustomReports by implementing your own reporting routes registration function and calling it instead of the above snippet. For instance, you can have:
private static void RegisterReportingRoutes(HttpConfiguration config)
{
config.Routes.MapHttpRoute(name: "Clients",
routeTemplate: "Controllers/{controller}/clients/{clientID}",
defaults: new { controller = "MyCustomReports", action = "Clients", clientID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "Instances",
routeTemplate: "Controllers/{controller}/clients/{clientID}/instances/{instanceID}",
defaults: new { controller = "MyCustomReports", action = "Instances", instanceID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "DocumentResources",
routeTemplate: "Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/resources/{resourceID}",
defaults: new { controller = "MyCustomReports", action = "DocumentResources" });
config.Routes.MapHttpRoute(
name: "DocumentActions",
routeTemplate: "Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/actions/{actionID}",
defaults: new { controller = "MyCustomReports", action = "DocumentActions" });
config.Routes.MapHttpRoute(
name: "DocumentPages",
routeTemplate: "Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/pages/{pageNumber}",
defaults: new { controller = "MyCustomReports", action = "DocumentPages" });
config.Routes.MapHttpRoute(
name: "DocumentInfo",
routeTemplate: "Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/info",
defaults: new { controller = "MyCustomReports", action = "DocumentInfo" });
config.Routes.MapHttpRoute(
name: "Documents",
routeTemplate: "Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}",
defaults: new { controller = "MyCustomReports", action = "Documents", documentID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "Parameters",
routeTemplate: "Controllers/{controller}/clients/{clientID}/parameters",
defaults: new { controller = "MyCustomReports", action = "Parameters" });
config.Routes.MapHttpRoute(
name: "Formats",
routeTemplate: "Controllers/{controller}/clients/{clientID}/formats",
defaults: new { controller = "MyCustomReports", action = "Formats" });
}
Note that you should reference the reports library from the solution containing the REST services.

Html.ActionLink generates the wrong URL, aslways adding Terminal/ to the URL

When I try to generate an HTML link using
#Html.ActionLink("Edit Carrier", "EditCarrier", "CustomerCare")
I would expect it to generate the URL /CustomerCare/EditCarrier/ but no matter what view I place it in the URL always gets generated as /Terminal/CustomerCare/EditCarrier/ and am I not sure why /Terminal/ is being added to the route. This is my first time NOT using Attribute Routing, and it is not an option to use it in this project. From looking around on the web I setup my RouteConfig.cs file as:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Terminals",
url: "Terminal/{controller}/{action}/{id}",
defaults: new { id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
What am I missing here?
This is because the route Terminals matches first when you request the url. If you don't use this route, then you can simply remove it from RouteConfig.cs, then the url will be /CustomerCare/EditCarrier/. If you need the Terminals route for any controller, you can add constraints to it:
routes.MapRoute(
name: "Terminals",
url: "Terminal/{controller}/{action}/{id}",
defaults: new { id = UrlParameter.Optional },
constraints: new { controller = #"ControllerWhoNeedsThisRoute" }
);
EDIT: Alternative you could also use #Html.RouteLink() and add the route name for generating the url. But then you need to specify the controller and the action in parameters. The second parameter is the name of the route to use:
#Html.RouteLink("Edit Carrier", "Default", new { controller = "CustomerCare", action = "EditCarrier" })

API Controller in /Controllers instead of /api

I have various projects in a solution (ASP.Net MVC, domain, database, entities and Telerik Report Server WebApi).
The ASP.Net MVC is hosted at hostname/ and the Report Server at hostname/ReportServer/
All works fine except the Report Server.
It places the ReportsController API at
/ReportServer/Controllers/ReportsController
instead of
/ReportServer/api/ReportsController
I have created a simple test Solution with just a ASP.Net MVC and ReportServer, in theory with the same settings. This works, but I can't see what is causing this. All the routings, base class etc are the same.
Any ideas?
Instead of calling the default Telerik Reporting Route Registration function (ReportsControllerConfiguration.RegisterRoutes), you can write your own route registration procedure and call it from WebApiConfig.Register (before the DefaultApi route). For example:
private static void RegisterReportingRoutes(HttpConfiguration config)
{
config.Routes.MapHttpRoute(name: "Clients",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}",
defaults: new { controller = "MyCustomReports", action = "Clients", clientID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "Instances",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}",
defaults: new { controller = "MyCustomReports", action = "Instances", instanceID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "DocumentResources",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/resources/{resourceID}",
defaults: new { controller = "MyCustomReports", action = "DocumentResources" });
config.Routes.MapHttpRoute(
name: "DocumentActions",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/actions/{actionID}",
defaults: new { controller = "MyCustomReports", action = "DocumentActions" });
config.Routes.MapHttpRoute(
name: "DocumentPages",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/pages/{pageNumber}",
defaults: new { controller = "MyCustomReports", action = "DocumentPages" });
config.Routes.MapHttpRoute(
name: "DocumentInfo",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/info",
defaults: new { controller = "MyCustomReports", action = "DocumentInfo" });
config.Routes.MapHttpRoute(
name: "Documents",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}",
defaults: new { controller = "MyCustomReports", action = "Documents", documentID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "Parameters",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/parameters",
defaults: new { controller = "MyCustomReports", action = "Parameters" });
config.Routes.MapHttpRoute(
name: "Formats",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/formats",
defaults: new { controller = "MyCustomReports", action = "Formats" });
config.Routes.MapHttpRoute( name: "EditingAndLayout", routeTemplate: "ReportServer/Controllers/{controller}/formats", defaults: new { controller = "MyCustomReports", action = "Formats" });
}
If the /ReportServer path is its own web application, instead of a route under the "/" application, then you can omit the "ReportServer/" part of the registration function above
The folder structure/name has no significance in case of ASP.Net Web API routing.
You don't need to put it in 'controller' folder or 'API' folder. If you noticed the routing configuration in WebAPI.config file, the default route template will be like 'api/{controller}/{id}' and this make your api routes working.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}", // <--
defaults: new { id = RouteParameter.Optional }
);

Web Api multiple get with same signature routing

I'm building a web api that has multiple get/post calls that have the same signatures. Now I know that in the case of multiple identical calls, you generally have 2 options: separate into different controllers, or use {action} in your routes. I have gone the {action} method as it fits best I believe in most of my controllers. However, in one of my controllers I would prefer not to use the action method.
I have a call like so:
[HttpGet]
public Program Program(string venue, string eventId)
//api/{controller}/{venue}/{eventId}
Now I need a new call
[HttpGet]
public Program ProgramStartTime(string venue, string eventId)
//api/{controller}/{venue}/{eventId}
I know I can add an action name to this and call i.e
api/{controller}/{action}/{venue}/{eventId}
But I feel like it breaks the expected. Is there a way that I could some something like
api/Content/LAA/1/PST
api/Content/LAA/1?PST
Also if I have to go the action route, I currently already have a route I use for other controllers, but it simply uses {id} as its only parameter. Will a new route conflict with this one? Is there a better way to setup my routes?
config.Routes.MapHttpRoute(
name: "...",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
config.Routes.MapHttpRoute(
name: "...",
routeTemplate: "api/{controller}/{action}/{venue}/{eventId}/{...}/{***}/{###}",
defaults: new {### = RouteParameter.Optional}
);
config.Routes.MapHttpRoute(
name: "...",
routeTemplate: "api/{controller}/{action}/{venue}/{eventId}/{...}",
defaults: new {... = RouteParameter.Optional}
);
config.Routes.MapHttpRoute(
name: "...",
routeTemplate: "api/{controller}/{action}/{venue}",
defaults: new {venue = RouteParameter.Optional}
);
I expect at least one method that would have up to 5 parameters
Here's the answer I found and it does pretty much exactly what I wanted:
config.Routes.MapHttpRoute(
name: "VenuesAllOrStream",
routeTemplate: "api/Racing/{action}",
defaults: new { controller = "Racing", action = "Venues" },
constraints: new { action = "Venues|All|Streaming" }
);
config.Routes.MapHttpRoute(
name: "VenueOrVideo",
routeTemplate: "api/Racing/{venue}/{action}",
defaults: new { controller = "Racing", action = "RaceNumbers" },
constraints: new { action = "RaceNumbers|Video" }
);
config.Routes.MapHttpRoute(
name: "ProgramOrMtp",
routeTemplate: "api/Racing/{venue}/{race}/{action}",
defaults: new { controller = "Racing", action = "Program" },
constraints: new { action = "Program|Mtp", race = #"\d+" }
);
It is important that the VenuesAllOrStream is first as otherwise the VenueOrVideo picks up the route. I most likely will extract out the action constraints into enums later.
Brief note : Setting the action default allows for the route to basically make it an optional parameter. So each route works without the {action} actually being set.

Resources