How do I generate a webapi url from an MVC view? - asp.net-mvc

Based on How to create ASP.NET Web API Url?
Neither of these plus a few more combinations of them I've tried are working
<li>CorpUser</li>
<li>CorpUser</li>
Here's my api config route table:
//https://stackoverflow.com/questions/11407267/multiple-httppost-method-in-mvc4-web-api-controller
config.Routes.MapHttpRoute(
name: "ApiReq",
routeTemplate: "webapi/{controller}/{action}",
defaults: null,
constraints: new { action = "Req" }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new
{
id = #"^\d*$",// Only integers
action = "^(?![rR]eq)$" //anything except req or Req
}
);
Why isn't the routing working? The RouteUrl returns empty string or null, the Action returns
<a href="http://localhost:11601/CorpUserRequest/Get">
instead of what the actual url should be http://localhost:11601/api/CorpUserRequest

You could use either of the following to generate links to Web API:
#Url.HttpRouteUrl("DefaultApi", new {controller = "corpuserrequest"})
#Url.RouteUrl("DefaultApi", new { httproute = true, controller = "corpuserrequest"})
Usually you would use option 1., but sometimes you might need to use 2. like in the following post (this post doesn't necessarily use RouteUrl, but the idea is that in places where you do not have a convenient extension like HttpRouteUrl, in this case Html.BeginForm, you can use httproute=true to indicate that you are indeed trying to generate a url to a Web API route)
ASP.Net WebApi: Invalid URL generated in BeginForm

Related

How to create URL for Logout action in WebApi

I am using Visual Studio 2017..... when I created the project, an AccountController was created with this action:
// POST api/Account/Logout
[Route("Logout")]
public IHttpActionResult Logout()
{
Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
return Ok();
}
On the other hand, this route was created by default:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I need to do a very simple thing. How can I get the Logout URL in a view?
I tried
#Url.Action("Logout", "Account", new { httproute = "DefaultApi" })
But it did not work since DefaultApi does not contain the action, causing the action to be added as a query string parameter.
If I don't use httproute property, the URL is built but without the "api" part causing the framework to not find it.
I have even tried
#Url.RouteUrl("DefaultApi", new { httproute = "Logout", controller = "Account" })">
with no success either.
You can define a new route to define your action name:
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
If you don't include the action name, Web API tries to find a suitable action for you based on your HTTP verb... for example if you send a Get request Web API tries to find an action starting with 'Get'... since your action name is Logout, the default API routing convention cannot match it to a request. see here for more info
Then this link should call the action (see here):
#Url.HttpRouteUrl("ActionApi", new {controller = "Account", action = "Logout"})

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.

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.

MVC 4 Web API Routing issues

This is my first rodeo with MVC Web API and I'm having some issues understanding the routing aspects. I would like to have a uri template similar to thise:
http://google.com/api/AzureQueue - GET for all items in the queue
http://google.com/api/AzureQueue/DeviceChart/ - GET returns devices and processing time for agent
http://google.com/api/{controller}/{id} <-- default
http://google.com/api/{controller}/{chartType}/{id} where ID is optional
where I'm struggling is:
1. what the french toash do I put in the WebApiConfig.cs file
2. do I need to do anthing special in my controller eg. specifiy NonActions & Actions, Action Names, etc
Any help is appreciated
You are almost there. The default route (in WebApiConfig.cs looks like this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
There's one very important caveat: the routes are examined in the order that they are declared with the first matching one being used, so the default route needs to go last.
With that out of the way, you need to make a decision, do you want the calls for various chart types to go to one action, or many?
For one action:
WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "AzureQueue",
routeTemplate: "api/AzureQueue/{chartType}/{id}",
defaults: new { controller = "AzureQueue", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
AzureQueueController.cs
public class AzureQueueController : ApiController
{
public string Get(string chartType)
{
return "chart = " + chartType;
}
public string Get(string chartType, int id)
{
return "chart = " + chartType + ",id = " + id.ToString();
}
}
There are two things to notice here. In the anonymous class assigned to defaults, the value for controller decides which controller to route the request to. This can either be in the route template, or simply defined in the class. Also, a request of type Get is automatically sent to an action that starts with Get and has the arguments in the Url that match the template (there are two different cases since id is optional).
This would be my preferred way to go unless the business logic for various charts is different.
On the other hand you could specify this:
WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "AzureQueue",
routeTemplate: "api/AzureQueue/{action}/{id}",
defaults: new { controller = "AzureQueue", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Since I'm using the word action is the template, this will get interpreted as an action name.
AzureQueueController.cs
[HttpGet]
public string DeviceChart()
{
return "chart = DeviceChart" ;
}
[HttpGet]
public string DeviceChart(int id)
{
return "chart = DeviceChart" + ",id = " + id.ToString();
}
Here there is no string argument, that part of the url is being used to decide which action (public method) to use. Also, since the action names don't start with Get, I need to add an attribute [HttpGet] for each method to mark them as being able to receive GET requests.
Good luck with your project.

ApiController GET Action naming

I have an ApiController with multiple GET Actions. The problem is that I wan't to name my actions without "Get" in the start of their names.
For instance, I can have an Action named "GetImage" and it will work just fine.
If I will name it "UpdateImage" it wont call the Action, because it probably want an explicit "Get" in the start of the action name.
I can solve it by defining different route for each action I want to use, but I am sure there must be an easier way achieving it.
I also tried [HttpGet] attribute and unfortunately it didn't do the trick.
My Route Config:
routes.MapHttpRoute(
name: "ImagesApi",
routeTemplate: "api/images/{action}/{id}",
defaults: new { controller = "ImageStorageManager",id = RouteParameter.Optional }
);
and I am accessing it by api/images/GetImage or api/images/UpdateImage
The way I've been creating api controller that aren't just for a single object might help you. I got the approach from John Papa's SPA talk on PluralSight (I highly recommend that for learning single page applications). He also walks through this in one of the modules.
It has 2 parts.
Part 1, setting up the routes to do the 2 normal scenarios and then added a 3rd for what i want:
// ex: api/persons
routes.MapHttpRoute(
name: ControllerOnly,
routeTemplate: "api/{controller}"
);// ex: api/sessionbriefs
// ex: api/persons/1
routes.MapHttpRoute(
name: ControllerAndId,
routeTemplate: "api/{controller}/{id}",
defaults: null, //defaults: new { id = RouteParameter.Optional } //,
constraints: new { id = #"^\d+$" } // id must be all digits
);
// ex: api/lookups/all
// ex: api/lookups/rooms
routes.MapHttpRoute(
name: ControllerAction,
routeTemplate: "api/{controller}/{action}"
);
Part 2, in the lookups controler (in John Papa's case), add an ActionName attribute to the methods:
// GET: api/lookups/rooms
[ActionName("rooms")]
public IEnumerable<Room> GetRooms()
{
return Uow.Rooms.GetAll().OrderBy(r => r.Name);
}
// GET: api/lookups/timeslots
[ActionName("timeslots")]
public IEnumerable<TimeSlot> GetTimeSlots()
{
return Uow.TimeSlots.GetAll().OrderBy(ts => ts.Start);
}
Decorate your action with [HttpGet]. See http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api for details on why, and how ApiController routing works.

Resources