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 }
);
Related
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.
How can I simplify these routes?
config.Routes.MapHttpRoute(
name: "GetUsersRoute",
routeTemplate: "api/User",
defaults: new { controller = "User", action = "GetUsers" }
);
config.Routes.MapHttpRoute(
name: "GetUserRoute",
routeTemplate: "api/User/{id}",
defaults: new { controller = "User", action = "GetUser", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "GetEmailAddressRoute",
routeTemplate: "api/User/GetEmailAddress/{id}",
defaults: new { controller = "User", action="GetEmailAddress", id = RouteParameter.Optional }
);
/*** Default ***/
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I needed to add the GetEmailAddressRoute because the controller didn't know which action to take for a path like api/User/5. However, since then former routes like api/User don't lead to the correct action.
Some action signatures from the controller:
public Object GetUsers() { };
public Object GetUser(int id) { };
public HttpResponseMessage GetEmailAddress (int id) { };
But now I'd need to add another route for the Post actions. Is there a simpler way?
Update: What helped me for posting new users was to remove the action from the first route. Now it is
config.Routes.MapHttpRoute(
name: "GetUsersRoute",
routeTemplate: "api/User",
defaults: new { controller = "User" }
);
You can take advantage of the new routing attribute feature coming with web api 2 :
[Route("api/User")]
public Object GetUsers() { };
[Route("api/User/{id}")]
public Object GetUser(int id) { };
[Route("api/User/GetEmailAddress/{id}")]
public HttpResponseMessage GetEmailAddress (int id) { };
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" }
);
Can I use the following two route rule together ?
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional } );
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Say by controller is = FruitApiController:ApiController and I wish to have the following
List<Fruit> Get() = api/FruitApi/
List<Fruit> GetSeasonalFruits() = api/FruitApi/GetSeasonalFruit
Fruit GetFruits(string id) = api/FruitApi/15
Fruit GetFruitsByName(string name) = api/FruitApi/GetFruitsByName/apple
Please help me on this. Thanks
You could have a couple of routes:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ApiById",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = #"^[0-9]+$" }
);
config.Routes.MapHttpRoute(
name: "ApiByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: null,
constraints: new { name = #"^[a-z]+$" }
);
config.Routes.MapHttpRoute(
name: "ApiByAction",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "Get" }
);
}
}
config.Routes.MapHttpRoute(
name: "ApiByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: null,
constraints: new { name = #"^[a-z]+$" }
);
I have 2 API routes for my API atm, but I want to add more, and the way I am doing it it seems to overwrite each other, so in the code I pasted, only the CreateUser route works.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "CreateUser",
routeTemplate: "api/{controller}/{cUser}",
defaults: new
{
controller = "User",
action = "CreateUser",
cUser = RouteParameter.Optional
});
routes.MapHttpRoute(
name: "AllGames",
routeTemplate: "api/{controller}/{playerId}",
defaults: new
{
controller = "Game",
action = "GetAllGames",
playerId = RouteParameter.Optional
});
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
}
I believe the pattern api/{controller}/{cUser} in "CreateUser" route is matching with rest of the controller actions because of its more generic pattern. Use specific controller name in the routes as "User" (api/User/{cUser}) and "Game" (api/Game/{playerId}). The more specific routes should be at the top and more generic at the bottom.
routes.MapHttpRoute(
name: "CreateUser",
routeTemplate: "api/User/{cUser}",
defaults: new
{
controller = "User",
action = "CreateUser",
cUser = RouteParameter.Optional
}
);
routes.MapHttpRoute(
name: "AllGames",
routeTemplate: "api/Game/{playerId}",
defaults: new
{
controller = "Game",
action = "GetAllGames",
playerId = RouteParameter.Optional
}
);