I've been trying to add a second POST method to the default ValuesController class that will take an id parameter and act identical to the PUT method, like so:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
namespace WebCalendar.Controllers {
public class ValuesController : ApiController {
// GET /values
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
// GET /values/5
public string Get(int id) {
return "value";
}
// POST /values
public void Post(string value) {
}
// POST /values/5
public void Post(int id, string value) {
Put(id, value);
}
// PUT /values/5
public void Put(int id, string value){
}
// DELETE /values/5
public void Delete(int id) {
}
}
}
Problem is, when I add this second post method, any time I make a POST request, I get the error:
"No action was found on the controller 'values' that matches the request."
If I comment out one of the methods (doesn't matter which one), POST will work with the other method. I've tried renaming the methods, and even using [HttpPost] on both of them, but nothing has worked.
How can I have more than one POST method in a single ApiController?
EDIT
Here is the only route that I'm using:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { controller = "values", id = RouteParameter.Optional }
);
You have to include the action in your route:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Related
This may be simple and its wierd and I cannot find out the reason why its hapenning.
I am using ASP.NET WEB API 2 Controller: I have a TransactionController, which suddenly stopped working after some modification in one of its action methods. I returned the code back, but its not working anymore.
All the other controllers are working normally. So, I decided two test it by creating two new controllers:
ValueController:
public class ValueController : ApiController
{
public List<string> GetValues()
{
return new List<string>() { "1", "2"};
}
public List<string> GetValues (int ID)
{
return new List<string>() { "1", "2" };
}
public List<string> GetValues(int UserID, DateTime CreateDate)
{
return new List<string>() { "1", "2" };
}
}
TransactionController:
public class TransactionController : ApiController
{
public List<string> GetTransaction(int ID)
{
return new List<string>() { "1", "2" };
}
}
This my webApiConfig
public static void Register(HttpConfiguration config)
{
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
When requesting the item controller by the url:
http://localhost:1607/api/value?ID=1, its hitting the GetValues(int ID) action
and the other actions are working fine.
But, the problem is still with the TransactionController. When requesting it using the url: http://localhost:1607/api/transaction?ID=1. Its not hitting the action.
This the response of the request:
And Its returning back the index view page in the home controller.
Any thoughts why this might happen?
I have the following WebApiConfig.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.EnableSystemDiagnosticsTracing();
// config.MessageHandlers.Add(new TokenValidationHandler());
// config.MessageHandlers.Add(new LoggingHandler());
config.Filters.Add(new ApiCustomExceptionHandler());
}
And I implemented the ExceptionFilterAttribute as follows
public class ApiCustomExceptionHandler : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var exceptionType = context.Exception.GetType();
if (exceptionType == typeof(UnauthorizedAccessException))
{
context.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
My controller is declared thus:
[ApiCustomExceptionHandler]
public class UserManagementController : ApiController
{
....
}
The problem is OnException never gets invoked, when I throw a UnAuthorizedAccessException. Any idea why this is so?
-Arun
When I had this problem, it turned out actually to be an problem on one action only -- other actions in the controller were fine. The problematic method was returning a generic (in my case,IEnumerable<Dictionary<string, object>>). I fixed this by returning an array instead: Dictionary<string, object>[].
Notice that returning a generic also gives warning CA1006 if you have Static Code Analysis enabled.
I have write the below configutation code in the webapiconfig.cs file for routing the multiple fintion. In the get methods I am getting the Multiple actions were found that match the request: System.String GetJobDetails(System.String) error.
Webapiconfig.cs code
config.Routes.MapHttpRoute(
name: "RendererAPi",
routeTemplate: "shared/{controller}/{id}",
defaults: new {id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "RendererAPiStatus",
routeTemplate: "shared/{controller}/{id}/status",
defaults: new { action = "getJobStatus", id = RouteParameter.Optional }
);
My controller code :
//post shared/rendererjob
[HttpPost]
public string createRendererJob(HttpRequestMessage request)
{
return "teststring";
}
//put shared/rendererjob/renderGUID
[HttpPut]
public string DoPutRequest([FromUri(Name="id")]string renderGUID)
{
return renderGUID;
}
//get shared/rendererjob/renderGUID
[HttpGet]
public string GetJobDetails([FromUri(Name = "id")]string renderGUID)
{
return renderGUID;
}
//get shared/rendererjob/renderGUID/status
[HttpGet]
public HttpResponseMessage getJobStatus([FromUri(Name = "id")]string jobid)
{
var response = Request.CreateResponse(HttpStatusCode.OK);
string uri = Url.Link("RendererAPiStatus", new { id = jobid });
response.Headers.Location = new Uri(uri);
return response;
}
the 3 URL are working fine, the //get shared/rendererjob/renderGUID[HttpGet] is not working and getting the multiple action error in the browser.
Any one please suggest me on this.
Note : the Route method is not working in the MVC4 VS2012, and unable to instal any patches into my system for this.
The problem is that you have two GET methods with the same signature, so you need to differentiate them somehow. Try adding a Default Action Name to your methods that use the RendererAPi route:
[HttpPost]
[ActionName("Default")]
public string createRendererJob(HttpRequestMessage request)
{
return "teststring";
}
//put shared/rendererjob/renderGUID
[HttpPut]
[ActionName("Default")]
public string DoPutRequest(string id)
{
return renderGUID;
}
//get shared/rendererjob/renderGUID
[HttpGet]
[ActionName("Default")]
public string GetJobDetails(string id)
{
return renderGUID;
}
//get shared/rendererjob/renderGUID/status
[HttpGet]
public HttpResponseMessage getJobStatus(string id)
{
var response = Request.CreateResponse(HttpStatusCode.OK);
string uri = Url.Link("RendererAPiStatus", new { id = id });
response.Headers.Location = new Uri(uri);
return response;
}
Then change the RendererAPi route as follows:
config.Routes.MapHttpRoute(
name: "RendererAPi",
routeTemplate: "shared/{controller}/{id}",
defaults: new {action = "Default", id = RouteParameter.Optional }
);
By the way you don't need to include the [FromUri] attribute for strings.
Per the WebApiContrib.Formatting.Jsonp GitHub readme, it appears that in the RouteConfig.cs this should be entered:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{format}",
defaults: new { id = RouteParameter.Optional, format = RouteParameter.Optional }
);
I currently don't have a RouteConfig.cs file in my AppStart. I created it using the Web API 2 template and I don't think I changed anything structurally. I do have a WebApiConfig.cs where I have set:
public static void Register (HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
how do I include it such that all routes have the ability to return Jsonp?
You could create a custom route attribute which implements IHttpRouteInfoProvider (which Web API route builder looks for when adding routes to route table) and then modify the template that is being generated by appending {format}
Example:
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
[CustomRoute(Order = 1)]
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}
[CustomRoute("{id}")]
public string GetSingle(int id)
{
return "value";
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class CustomRouteAttribute : Attribute, IHttpRouteInfoProvider
{
public CustomRouteAttribute()
{
Template = String.Empty;
}
public CustomRouteAttribute(string template)
{
if (template == null)
{
throw new ArgumentNullException("template");
}
if (template == string.Empty)
{
Template = template + "{format?}";
}
else
{
Template = template.TrimEnd('/') + "/{format?}";
}
}
public string Name { get; set; }
public int Order { get; set; }
public string Template { get; private set; }
}
I found this comment in a pull request but I don't understand if this is yet implemented into the production package nor if it got pulled at all.
If you are using Attribute Routing, you should add "/{format}" after each route if you plan to use the URI mapping for jsonp, e.g. [Route("api/value/{id:int}/{format?}")]. If you will require the Content-Type header to specify text/javascript, then you can leave your routes alone. (See the sample applications for examples.)
I have a ASP.NET MVC application in VS 2010. I added a new Web API Controller to my application. Here is the simple method I am trying to call:
public List<Article> Get()
{
using (var db = new HighOnCodingDbEntities())
{
var articles = (from a in db.Articles
select a).Take(10).ToList();
return articles;
}
}
Global.asax:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
When I call this method I get "Resource Not Found". I have published the application binary to the production and I believe that is all I need to do.
URL should be: http://www.highoncoding.com/api/articlesapi
ArticlesAPIController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using HighOnCoding.Models;
namespace HighOnCoding.Controllers
{
public class ArticlesAPIController : ApiController
{
// GET api/<controller>
public List<Article> Get()
{
using (var db = new HighOnCodingDbEntities())
{
var articles = (from a in db.Articles
select a).Take(10).ToList();
return articles;
}
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post(string value)
{
}
// PUT api/<controller>/5
public void Put(int id, string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
Works on local machine:
In production, ensure that the .NET Framework version for your IIS7 Application Pool for your website is set to .NET 4.0.xxx in integrated mode.