ASP.net Web API 2 custom methods - asp.net

I've added this method to my web api controller:
[HttpPost]
public bool CreateTrening(int routineId)
{
try
{
var userId = User.Identity.GetUserId();
TreningService.CheckIfLastTrenigWasCompleted(userId);
TreningService.CreateTreningForUser(userId, routineId);
return true;
}
catch (Exception ex)
{
return false;
}
}
And I've added another route to my WebApiConfig file, so it looks like this now:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But when I try to call my method:
/EditWorkout/CreateTrening/1
I get this error:
{"Message":"No HTTP resource was found that matches the request URI '/EditWorkout/CreateTrening/1'.","MessageDetail":"No action was found on the controller 'EditWorkout' that matches the request."}
How can I call my method which is in WebApi controller ?

Calling with that URL to a post method will not work because the post expects the values transmitted in the Http Message body. not in the URL so your controller is not finding the method.
Probably the best solution for this is to encode your posted value in the http message you send the controller, and call the URL with /EditWorkout/CreateTrening, this will work as a post.
Here is another SO thread where the question of how to do this was answered,
Specific post question answered

Related

MVC action results in 404 with "No type was found that matches the controller" invoking default action on controller works

I inherited some ASP.Net MVC code and am tasked with adding some new features. I am a complete beginner using ASP.Net MVC and come from a background of mainly using Web Forms.
I added a new controller (ApiController) and I added the following actions to it:
// GET: /Api/Index
public string Index()
{
return "API Methods";
}
// GET: /Api/DetectionActivity
public JsonResult DetectionActivity()
{
var detections = from d in db.Detections
orderby DbFunctions.TruncateTime(d.CreationTime)
group d by DbFunctions.TruncateTime(d.CreationTime) into g
select new { date = g.Key, count = g.Count() };
ViewBag.DetectionCounts = detections.ToList();
return Json(detections, JsonRequestBehavior.AllowGet);
}
My RouteConfig.cs has the following registered routes.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Dashboard", action = "Index", id = UrlParameter.Optional }
);
This looks like the tutorials I've been reading but it's not working and I'm probably missing something.
If I go to localhost:21574/api I see the output from the Index() action, "API Methods".
If I go to localhost:21574/api/DetectionActivity it throws a 404 with the following data in the response:
{
"Message":"No HTTP resource was found that matches the request URI 'http://localhost:21574/Api/DetectionActivity'.",
"MessageDetail":"No type was found that matches the controller named 'DetectionActivity'."
}
I'm thinking there is something I need to do that I'm not.
Any suggestions on what to do next?
Update 1
I tried this with my RouteConfig.cs
routes.MapRoute(name: "ApiController",
url: "{controller}/{action}"
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Dashboard", action = "Index", id = UrlParameter.Optional }
);
These were my results:
If I go to localhost:21574/api I see the output from the Index() action, "API Methods". Same as before.
If I go to localhost:21574/api/DetectionActivity it throws a 404 with the following data in the response:
{
"Message":"No HTTP resource was found that matches the request URI 'http://localhost:21574/Api/DetectionActivity'.",
"MessageDetail":"No type was found that matches the controller named 'DetectionActivity'."
}
Same as before.
If I go to localhost:21574/Api/Api/DetectionActivity it throws a 404 with this data in the response:
{
"Message":"No HTTP resource was found that matches the request URI 'http://localhost:21574/Api/Api/DetectionActivity'.",
"MessageDetail":"No type was found that matches the controller named 'Api'."
}
Now it's saying it can't find a controller named "Api".
from your Route Config
the URL should be: localhost:21574/Api/Dashboard/DetectionActivity
or if you really need localhost:21574/Api/DetectionActivity (not recommended)
change your Register method in WebApiConfig class to
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{action}/{id}",
defaults: new { controller = "Dashboard", action = "Index", id = RouteParameter.Optional }
);

Web API put method keep returning 404

Using C# web api, i created a web api that update the information.
// Update
// URI PUT + api/Gender/id
[HttpPut]
public IHttpActionResult PutGender(int id, [FromBody] Gender g)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var query = (from gen in genders
where gen.Id == id
select gen).FirstOrDefault();
if (query == null)
{
return BadRequest("wrong");
}
query.Description = g.Description;
query.LanguageId = g.LanguageId;
query.InternalCode = g.InternalCode;
query.isActive = g.isActive;
query.UpdatedAt = DateTime.Now;
return StatusCode(HttpStatusCode.OK);
}
I am testing through fiddler and this is how i ran the thing:
URL http://localhost:49625/API/PutGender
Action PUT
Request Header
Content-Type: application/json
User-Agent: Fiddler
Host: localhost:49625
Content-Length: 86
RequestBody
{"Id":1,"Description":"Undecided","LanguageId":null,"InternalCode":0,"isActive":false}
The URL is missing the name of the controller. For example, if your controller is called GenderController then the URL should be http://localhost:49625/API/Gender/PutGender
For other people that might run into this problem (myself included). Also check your route configuration. If you are using the default route configuration:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
There is no need to specify the "action part" of your url (PutGender), "PUT http://myhost/api/gender/1" should be fine. Also, if your method is named Put...Something, I am pretty sure you can omit the [HttpPut] attribute from the method signature aswell.

Web api routes with multiple actions with different parameters

In a ASP.NET Web api project I have a VacationController where I want to use these action methods.
How can I construct the routes to achieve this?
public Enumerable<Vacation> GetVacation()
{
// Get all vactions
return vacations;
}
public Vacation GetVacation(int id)
{
// Get one vaction
return vacation;
}
public Enumerable<Vacation> ByThemeID(int themeID)
{
// Get all vactions by ThemeID
return vacations;
}
I would like the URL's to look like this
/api/vacation // All vacations
/api/vacation/5 // One vacation
/api/vacation/ByThemeID/5 // All vacations from one theme
Edit 30-10-2013
I have tried Pasit R routes but I can't getting to work. I verily tried every combination I could think of.
This is what I have know. As you can see I have added a extra parameter at the bein of the route. I realized that I needed that in order to seperate the Vacations sold onder different labels.
Here are the routes I use. and the work OK for these URL's
/api/vacation // All vacations
/api/vacation/5 // One vacation
/api/vacation/ByThemeID/5 // All vacations from one theme
But it dosn't work for the last URL
config.Routes.MapHttpRoute(
name: "DefaultApiSimbo",
routeTemplate: "api/{label}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And here my Action method in the VacationController
// ByThemeID api/{label}/Vacation/ByThemeId/{id}
[HttpGet]
public IEnumerable<Vacation> ByThemeID(string label, int id)
{
return this.repository.Get(label);
}
// GET api/{label}/Vacation
public IEnumerable<Vacation> GetVacation(string label)
{
return repository.Get(label);
}
// GET api/{label}/Vacation/{id}
public Vacation GetVacation(string label, int id)
{
Vacation vacation;
if (!repository.TryGet(label, id, out vacation))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
return vacation;
}
Can some one give my a push in the right direction ;-)
Thanks in advance
Anders Pedersen
Assuming that the class is name VacationController, then a default routes for these methods would look something like:
/api/Vacation/GetVacation
/api/Vacation/GetVacation?id=1
/api/Vacation/ByThemeID?id=1
This is all assuming that the routing has note been updated.
add defaults action = "GetVacation" and make id as optional
the ApiController base class could handle overload GetVacation() and GetVacation(int id) selection automatically.
to register WebApiConfig
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{*param}",
defaults: new { action = "Get", param = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "Vacation",
routeTemplate: "api/vacation/{action}/{*id}",
defaults: new { controller = "Vacation", action = "GetVacation", id = RouteParameter.Optional }
);
}

Web API Nested Resource in URL

I am creating my first ASP.NET web API. I am trying to follow the standard REST URLs. My API would return the search result records. My URL should be –
../api/categories/{categoryId}/subcategories/{subCategoryId}/records?SearchCriteria
I am planning to use oData for searching and Basic / Digest Authentication over IIS. My problem is in the nested resources. Before I return the search results, I need to check whether the user has access to this category and sub category.
Now I created my Visual Studio 2012 – MVC4 / Web API project to start with. In the App_Start folder, there are 2 files that I believe are URL and order of resource related.
1.RouteConfig.cs
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
2.WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
With this model, it works fine if my URL is ../api/records?SearchCriteria but it is not my URL design mentioned above. I understand that I have to do little more reading but so far not able to find the correct article. Need your advice on how to achieve my URL and what changes are needed in these 2 files. Alternatively, are there some other configuration that I am missing here? Thanks in advance.
Asp.net Web API 2 provides Attribute routing out of the box. You can define Route on individual action method or at global level.
E.g:
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
You can also set a common prefix for an entire controller by using the [RoutePrefix] attribute:
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET api/books
[Route("")]
public IEnumerable<Book> Get() { ... }
// GET api/books/5
[Route("{id:int}")]
public Book Get(int id) { ... }
}
You can visit this link for more information on Attribute routing in Web API 2.
Assuming you have a controller named categories, Your WebApiConfig.cs could have a route like this to match your desired url (I would personally leave the /records portion off):
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{categoryId}/subcategories/{subCategoryId}",
defaults: new { controller = "categories", categoryId = somedefaultcategory,
subCategoryId = RouteParameter.Optional }
);
and a method could look like this:
// search a single subcategory
public IQueryable<SearchRecord> Get(int categoryId, int subCategoryId = 0, string SearchCriteria = "")
{
// test subCategoryId for non-default value to return records for a single
// subcategory; otherwise, return records for all subcategories
if (subCategoryId != default(int))
{
}
}
But, what if you want to also return just the categories and not subcategories? You'd need an additional route after the first one that is more generic:
config.Routes.MapHttpRoute(
name: "Categories",
routeTemplate: "api/{controller}/{categoryId}",
defaults: new { controller = "categories", categoryId = RouteParameter.Optional }
);
with two methods like:
// search a single category
public IQueryable<SearchRecord> Get(int categoryId, string SearchCriteria = "")
{
}
// search all categories
public IQueryable<SearchRecord> Get(string SearchCriteria = "")
{
}

ASP.NET Web API in WebForms 404

I have a controller called "UploadsController". I have a GET action like so:
public string GetUpload([FromUri]string action)
{
return "hey " + action;
}
If I navigate to the following API URL in my browser, I get a successful response.
http://localhost:52841/MySite/api/uploads?action=testaction
However, when I try calling the API from code-behind in my WebForms app, I get a 404 response.
Here's what I have in my Global.aspx file (even though I believe the first should do it):
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
RouteTable.Routes.MapHttpRoute(
name: "Default2Api",
routeTemplate: "api/{controller}/{action}",
defaults: new { controller = "Uploads", action = "GetUpload" });
Here's how I'm calling the API:
// Send a request asynchronously continue when complete
client.GetAsync("http://localhost:52841/MySite/api/uploads?action=testaction").ContinueWith(
(requestTask) =>
{
// Get HTTP response from completed task.
HttpResponseMessage response = requestTask.Result;
// Check that response was successful or throw exception
response.EnsureSuccessStatusCode();
// Read response asynchronously as JsonValue
response.Content.ReadAsAsync<string>().ContinueWith(
(readTask) =>
{
var result = readTask.Result;
//Do something with the result
});
});
I thought I've done this before (with the RC version, using RTM now), but I can't seem to get this one.
As a side note, the request isn't showing in fiddler for some reason, which is kind of annoying when you're trying to debug these kind of stuff.
Any help is appreciated.
Try naming your query string parameter to something else (Right now it is "action"). I think that's one reason it's causing problems. Since MVC is convention-based, that might be causing problems.
Also in your route declaration, try adding the query string parameter (Let me call it custAction).
And declare custom route before default route.
Code sample:
RouteTable.Routes.MapHttpRoute(
name: "Default2Api",
routeTemplate: "api/{controller}/{action}",
defaults: new { controller = "Uploads", action = "GetUpload", custAction = RouteParameter.Optional});
Yes I have been through the same problem most likely your issue is that webapi doesnt allow cross domain calls by default or at least this is what I know about it.
You need to add a CORS support to your web api code, follow the link this guy has shown how to add CORS to your webapi
http://code.msdn.microsoft.com/CORS-support-in-ASPNET-Web-01e9980a
Good Luck.

Resources