C# Web API 405 Error on GET - c#

I'm brand new to restful APIs after a decade on desktop development. I'm a little confused as to why I am getting a 405 attempting a GET for a controller.
My controller:
public class ApplicantsController : ApiController
{
/// <summary>
/// Gets the details of the applicant and their application
/// </summary>
/// <param name="applicantID">The ID of the applicant to get the most recent application and details for</param>
/// <returns></returns>
public HttpResponseMessage Get(int applicantID)
{
try
{
using (DbQuery query = new DbQuery("SELECT * FROM Applicants AS A WHERE A.ID = #ApplicantID",
new DbParam("#ApplicantID", applicantID)))
{
using (DataTable data = query.ExecuteDataTable())
{
if (data.Rows.Count > 0)
{
Applicant applicant = new Applicant(data.Rows[0]);
return new HttpResponseMessage()
{
Content = new StringContent(applicant.ToJson(), Encoding.UTF8, "text/html")
};
}
}
}
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
catch (Exception ex)
{
Methods.ProcessException(ex);
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
public HttpResponseMessage Post(Applicant applicant)
{
if (applicant.Save())
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, applicant);
string uri = Url.Link("DefaultApi", new { id = applicant.ID });
response.Headers.Location = new Uri(uri);
return response;
}
return Request.CreateResponse(HttpStatusCode.InternalServerError, "Error saving applicant");
}
}
}
I have the same default routing in my WebApiConfig and confirmed that the way my controller is written it matches a standard Web API 2 controller with read, write, update methods. I've tried using DefaultAction, I've tried decorating methods with [HttpGet] and [AcceptVerbs]. Whenever I try to access the Get through either a browser myself or through ajax, I get a 405 (method not allowed).
Ajax test:
$("#TestGetApplicantButton").click(function (e) {
e.preventDefault();
alert("Getting Applicant...");
$.ajax({
type: "GET",
url: "/api/Applicants/108",
contentType: "application/json; charset-utf-8",
dataType: "json",
success: function (data) {
$("#ResponseDiv").html(JSON.stringify(data));
},
failure: function (errMsg) {
alert(errMsg);
}
});
});
Ajax works perfectly for all of the other controllers, showing the data returned (example: and it even calls the Post method on this controller just fine. Yet I can't get my Get to work. I don't see where I could be going wrong.
My routing:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
}
I've googled and checked here, but everyone seems to only have issues with POST, PUT, or DELETE, so I haven't found an answer for this. I've also tried removing the POST method in the controller - that gets me a 404 instead (not from my 404, I confirmed the code doesn't execute), which suggests for some reason the routing can't find my get method at all.

You need to add a default value to your applicantID parameter, since your route has the first parameter marked as RouteParameter.Optional.
public HttpResponseMessage Get(int applicantID = 0)
This will ensure that your Get method signature matches your "DefaultApi" route.

Related

How to pass multiple type of parameters in web api from angularjs?

I am struggling pretty badly in passing parameters to my web api.
There are two case I will list them both.
I want to pass a simple string param like this below
[HttpGet]
public string VerifyName(string name)
{
return name + "hi";
}
For which I am creating a url like this in my angularjs controller.
var name = "hello";
var msg = "";
$http.get('/api/VisitorWeb/VerifyName', name).success(function (data) {
msg = data;
}).error(function (data) {
$scope.error = "An error has occured while adding! " + data;
});
This keeps returning 404. Also
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:43516/api/VisitorWeb/VerifyName'.","MessageDetail":"No action was found on the controller 'VisitorWeb' that matches the request."}
Similarly when I am trying to pass a object it is giving the same result
My angular function
var loginModel = {
UserName: $scope.UserName,
PassWord: $scope.Password
};
var msg = "";
$http.get('/api/VisitorWeb/VerifyLogin', loginModel).success(function (data) {
msg = data;
}).error(function (data) {
$scope.error = "An error has occured while adding! " + data;
});
Web Api Method
[HttpGet]
public string VerifyLogin(UserLoginDomainModel loginModel)
{
//do some business logic
return "abc ";
}
Response is 404.
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}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
I am thinking there is some problem with the routing that is basic but some how not able to figure out what it is , Please suggest
This action:
[HttpGet]
public string VerifyName(string name)
{
return name + "hi";
}
defined without attribute routing is mapped to the standard convention routing into the following URL:
http://yourhost/apiVisitorWeb/VerifyName?name=myNameValue
Because your route template "api/{controller}/{action}/{id}" expects a parameter named id (not name) to be part of the URI.
If you want to keep using your routing as is either use the above URI or change the name of your parameter into id, so you should be able to use this address:
http://yourhost/apiVisitorWeb/VerifyName/myNameValue
The same thing applies to your complex type parameters: in GET actions any complex parameter is expected to be bound from the URI as a collection of query string parameters. Your second action will bind to the following URI:
http://yourhost/apiVisitorWeb/VerifyLogin?UserName=userNameValue&PassWord=myPassword
But this is a bad practice for many reasons (security on the top). I strongly suggest you to turn this kind of action into a POST action, so you can send your model as the body of the request.
Give this a try:
$http({ method: "GET", url: "/api/VisitorWeb/VerifyName", params: { name: name} }).success()

webapi post method with parameters doesn't work

When I call my webAPI controller that contains a post method with NO parameters it goes to the method. However, when I pass parameters (and when I update the api controller with paramters as well) into this see the snippet below the 1st snippet I get the 405 error that it doesn't support POST.
var captchURL = "/api/Captcha";
$.ajax({
url: captchURL,
dataType: 'json',
contentType: 'application/json',
type: 'POST'
})
var jsondata = {solution: "7", answer: "7"};
var captchURL = "/api/Captcha";
$.ajax({
url: captchURL,
dataType: 'json',
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(jsondata)
})
UPDATE - Controller Code:
public class CaptchaController : ApiController
{
private readonly ICaptchaService _service;
public CaptchaController(ICaptchaService service)
{
_service = service;
}
public Captcha Get()
{
return _service.Get();
}
[HttpPost]
public bool Post(string solution, string answer)
{
return _service.Post();
}
}
UPDATE - WebApiConfig:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Is it because I don't have the solution and answer params (in my WebApiConfig) that it doesn't recognize them?
What am I doing incorrectly?
This is a slightly different method of setting the route, but I prefer it.
In your controller code add a route prefix and routes for each method that will represent a POST of GET request...
[RoutPrefix("Captcha")]
public class CaptchaController : ApiController
{
[Route("Post/{solution}/{answer}")]
[HttpPost]
public bool Post(string solution, string answer)
{
return _service.Post();
}
}
This should work as long as you are setting the path correctly, using the correctly typed parameters, and returning a correctly typed value. If you use a Model then you do not have to add the parameters into the Route path.
This worked for me when I was setting up my WebAPI. If anyone sees anything wrong with explanation please let me know. I am still learning (and always will be), but just wanted to let you know what I did that worked.
create a model
public class Captcha {
public string solution { get; set; }
public string answer { get; set; }
}
the controller is this
[HttpPost]
public string Post([FromBody] Captcha cap)
{
return cap.solution + " - " + cap.answer;
}
Add another MapHttpRoute which will accept 'solution' and 'answer' as parameters
sample :
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{solution}/{answer}",
defaults: new { solution = RouteParameter.Optional, answer= RouteParameter.Optional }
);
[HttpPost]
public bool Post(Captcha cap)
{
return _service.Post();
}
And
Change data: JSON.stringify(jsondata) to data : jsondata .
The Reason it doesn't work is because there is not route that will accept 2 parameter at your [post] address - api/captcha/.
you have 2 options, either set up attribute routing in controller as
[Route("Post/{solution}/{answer}")]
[HttpPost]
public bool Post(string solution, string answer)
{
return _service.Post();
}
or create a model as
Public class CaptchaModel
{
Public string Solution {get; set;}
Public string Answer {get; set;}
}
And in your action method
[HttpPost]
public bool Post([FromBody] CaptchaModel model)
{
var solution = model.solution;
var answer = model.answer;
.........
return _service.Post();
}
According to www.asp.net,
"when an HTTP method is configured for use on the server, but it has been disabled for a given URI, the server will respond with an HTTP 405 Method Not Allowed error."
Further reference on 405
http://www.asp.net/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications
You must remove JSON.stringify(data) from your request and then add [FromBody] in front your model. Then it will work.

404 Not found on C# WebApi

I have an class that inherits from ApiController, some of its methods are called properly, some others are Not found. I can't find out why. I've been looking for a solution for hours now, still not getting it. Note That I'm new at this, it's my first WebApi in C#.
Routing: (WebApiConfig.cs)
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Configuration et services API Web
// Itinéraires de l'API Web
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Controller:
public class ExchangeController : ApiController
{
public HttpResponseMessage GetMailHeader(int id)
{
Console.WriteLine(id);
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new StringContent("ok");
return response;
}
public HttpResponseMessage GetTest()
{
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new StringContent("working !!");
return response;
}
}
JS:
$.ajax({
type: "GET",
url: "/api/exchange/getTest",
done: function (data) {
console.log(data);
}
});
$.ajax({
type: "GET",
url: "/api/exchange/getMailHeader",
data: "42",
done: function (data) {
console.log(data);
}
});
The getTest method returns a 200 OK while the getMailHeader returns 404 Not Found. What did I miss ?
As I understand it, data adds a query string, not a part of the url itself. You define the id to be part of the url, so the right url is /api/exchange/getmailheader/42.
You can also move id out of the routeTemplate.
Because your method starts with 'Get', and does not have a specific attribute, the framework assumes its an HttpGet (see rule 2 below), which requires the id to be part of the url (based on the default route).
If you want it to be an HttpPost (where you pass a json object in the body like you are doing now), then add a [HttpPost] attribute above your method or remove the 'Get' portion of the action name
Reference
HTTP Methods. The framework only chooses actions that match the
HTTP method of the request, determined as follows:
You can specify the HTTP method with an attribute: AcceptVerbs,
HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost, or
HttpPut.
Otherwise, if the name of the controller method starts with "Get", "Post", "Put", "Delete", "Head", "Options", or "Patch", then
by convention the action supports that HTTP method.
If none of the above, the method supports POST.
Thanks to everyone for your comments and answers, it has led me to the solution.
I was miss writing my ajax request. I didn't get any printed data on the console from console.log, and as #Ahmedilyas said, the data property was badly written.
The following works :
$.ajax({
type: "GET",
url: "/api/exchange/getTest"
})
.done(function (data) {
console.log(data);
});
$.ajax({
type: "GET",
url: "/api/exchange/getMailHeader",
data: { id: 42 }
})
.done(function (data) {
console.log(data);
});

Ho to access the Action from WebApiController with many actions and same parameters

I can't see the trouble, i have the ApiController with many actions, when i try to do request to one of action i have the following exception. Thanks in advance
//Angular script
var app = angular.module('app');
app.factory('userRepository', function ($http) {
var defaultUrl = 'api/UsersApi';
return {
addUserToRole: function (data) {
return $http({
method: 'POST',
url: defaultUrl + '/' + 'AddUserToRole',
data: {
RoleId: data.RoleId,
UserId: data.User.UserId
}
});
}
}
});
//Api controller
public class UsersApiController : ApiController
{
UserRepository uRep;
RoleRepository rRep;
[Authorize]
[HttpPost]
public HttpResponseMessage AddUserToRole(Int32 RoleId, Int32 UserId)
{
try
{
uRep = new UserRepository();
uRep.AddUserToRole(UserId, RoleId);
}
catch (Exception ex)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
}
//Route configuration
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi1",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi2",
routeTemplate: "api/{controller}/{action}"
);
}
}
After request i have the result:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
The requested resource does not support http method 'GET'.
if i change HttpPost to HttpGet appears the following error occurs
This XML file does not appear to have any style information associated with it. The document tree is shown below.
No HTTP resource was found that matches the request URI '/api/UsersApi/AddUserToRole'.
No action was found on the controller 'UsersApi' that matches the request.
In your addUserToRole function inside your service, instead of setting data with the post set params:
params: {RoleId: data.RoleId,UserId: data.User.UserId}
Also, check your Web API route config to make sure you're allowing multiple requests with the same HTTP method to one controller, do this by including the action name inside your route config.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

Asp Web Api async action - 404 error

I've got some api controller with this action:
public class ProxyController : ApiController {
public async Task<HttpResponseMessage> PostActionAsync(string confirmKey)
{
return await Task<HttpResponseMessage>.Factory.StartNew( () =>
{
var result = GetSomeResult(confirmKey);
return Request.CreateResponse(HttpStatusCode.Created, result);
});
}
}
And here is my api routing confuguration:
routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
When I try to make any Post/Get Requests to this action, it's returns '404' error. How can I fix it? All other not-async actions in this controller works fine.
UPD. JS query:
$.ajax({
url: Url + '/api/Proxy/PostActionAsync',
type: 'POST',
data: { confirmKey: that.confirmKey },
dataType: 'json',
xhrFields: { withCredentials: true },
success: function (data) {
............
},
error: function (jqXHR, textStatus, errorThrown) {
............
}
});
UPD. Resolved by adding [FromBody] to my parameters in action method just like in J. Steen's answer, now it's look's like
public class ProxyController : ApiController {
public async Task<HttpResponseMessage> PostActionAsync([FromBody]string confirmKey)
{
var someModel = new SomeResultModel(User.Identity.Name);
await Task.Factory.StartNew(() => someModel.PrepareModel(confirmKey));
return Request.CreateResponse(HttpStatusCode.OK, someModel);
}
}
And it works!
The routing configuration for Web API works a little differently than MVC.
Try
routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });
Note the missing {action} as that is resolved by Web API at call-time, automagically, depending on the HTTP verb you use for your request.
Consider this article on Web API routing which lists (as an example):
HTTP Method URI Path Action Parameter
GET api/products GetAllProducts (none)
GET api/products/4 GetProductById 4
DELETE api/products/4 DeleteProduct 4
In your case, the async version of an action is also resolved automatically.
POST api/products PostActionAsync (Post data)
Since we now know the controller name, requests would be like:
GET api/proxy
GET api/proxy/4
POST api/proxy (with post data)
Edit:
After additional research (brief, I admit) I have found the issue.
You need to add [FromBody] infront of your in-parameter.
public async Task<HttpResponseMessage> PostActionAsync([FromBody] string confirmKey)
This, combined with sending just the value (no wrapping json) works wonders. Set content type to "application/x-www-form-urlencoded" instead of json, and send your parameter as "=" + that.confirmKey.
Alternative:
If you don't want to fiddle around with content-types and prefixing =-signs, just send it as part of the querystring. Forget the [FromBody] and the rest. Call
/api/Proxy/PostActionAsync?confirmKey=' + that.confirmKey
Additional, exhaustive information in this blog.
Is that change possible?
public async Task<HttpResponseMessage> PostActionAsync()
{
var result = await GetSomeResult();
return Request.CreateResponse(HttpStatusCode.Created, result);
}

Categories