Web API put method keep returning 404 - asp.net

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.

Related

Why can you not edit records with Web API 2? Is it not possible?

No one knows how to edit records! Does no one perform this action? The POST, GET and DELETE all work but PUT, regardless of what you do, does not work. Does no one edit records using an Web API2 from ASP.NET?
I have used both Hurl.it and POSTMAN and neither one of them can perform an edit using PUT. It just produces a 400 error - there is no information about the error because there is nothing wrong with the request!
Is there someone in the world who has managed to edit records with this? When the bounty becomes available I will give as much as I can - please someone inform us how this action can be performed.
How has anyone not even noticed that this API does not work fully? I do nto know where else to ask! No one on the ASP.NET forums knows how to do it either.
HURL.IT
yoururl/api/objects/id
parameter fieldname: value (all)
Click "Launch request"
The field gets edited
In Web API 2 - 400 error... no other information (because there is nothing wrong with request)
POSTMAN - same as above (more or less)
Code inside controller:
// PUT: api/Table1s/5
[ResponseType(typeof(void))]
public IHttpActionResult PutTable1(int id, Table1 table1)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != table1.TestID)
{
return BadRequest();
}
db.Entry(table1).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!Table1Exists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
If your are using Attribute Routing, your controller could look something like this...
[RoutePrefix("api/Table1s")]
public class Table1Controller : ApiController {
// PUT: api/Table1s/5
[HttpPut]
[Route("{id:int}")]
[ResponseType(typeof(void))]
public IHttpActionResult PutTable1(int id, [FromBody]Table1 table1) {...}
}
A request to that action might look like ...
PUT http://localhost:5076/api/Table1s/5 HTTP/1.1
User-Agent: Fiddler
Host: localhost:5076
Content-Type: application/json
Content-Length: 55
{
"fieldname1":"value1",
"fieldname2":"value2"
}
Make sure that you configured attribute routing along with your default configuration
//....
config.MapHttpAttributeRoutes()
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
);
//....

How to call a web api from url in mvc4

I have created a web api with two parameters. I want to test it from url that its working or not. I have tried but not able to call. My code is.
API routing...
public static void Register(HttpConfiguration config)
{
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
[HttpGet]
public List<Library> GetLibrary(string DataType, int DataId)
{
List<Library> LibraryList = new List<Library>();
if (DataType == "Course")
{
using (ICA.LMS.Service.Models.Entities dbCourse = new Models.Entities())
{
LibraryList = (from c in dbCourse.COURSELIBRARies
where c.LIBITEMID == DataId
select new Library { Id = c.LIBITEMID, Name = c.GROUPNAME, Desc = c.DESCRIPTION }).ToList();
}
}
return LibraryList;
}
Url which i am putting in browser.
http://localhost:1900/api/librarybyid/?DataType='Course'&DataId=1
Result i am getting...
<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://localhost:1900/api/librarybyid/?DataType='Course'&DataId=1'.
</Message>
<MessageDetail>
No type was found that matches the controller named 'librarybyid'.
</MessageDetail>
</Error>

ASP.NET Web API Controller Function Executes when entering not mapped action route

I have a controller function which accepts a strongly typed model as parameter.
When i enter ANY url mapping to the controller but not on a specific action on the post request ,
then the controller executes this function instead of returning a 404 code.
When i Change the function's parameter to a primitive type variable then the problem does not occur. (i have tried using other strongly typed models as parameters and again the problem occurs)
Here's the function.
public class PhoneApiController : ApiController
{
[HttpPost]
public HttpResponseMessage RegisterApp(RegisterAppInfo appInfo)
{
var resp = Request.CreateResponse(HttpStatusCode.OK, new
{
Success = true,
AppId = 1000,
IdAlias = "QAUBC9",
appInfo = appInfo
});
return resp;
}
}
So when i enter for example
localhost:51464/Api/PhoneApi/Sajsdkasjdklajsasd
the function executes normally.!
I am using the default Route config
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I don't know if this is a bug or i am doing something wrong.
The URI /Api/PhoneApi/Sajsdkasjdklajsasd does match your route template api/{controller}/{id} with {controller} matching PhoneApi and {id} matching Sajsdkasjdklajsasd. I assume you are making a POST to this URI. So, Web API is mapping your request to the action method RegisterApp with [HttpPost] in the controller class PhoneApiController.
As far as the junk stuff in the URI, it gets mapped to {id}. But your parameter is RegisterAppInfo, which is a complex type and that gets bound from request body and not the URI. That's why it works when you have the complex type. The simple types are bound from URI, query string.
If you have the action method as public HttpResponseMessage RegisterApp(string id, Abc appInfo), you will see that this id parameter gets populated with "Sajsdkasjdklajsasd".
For MVC 4.5 this is the only thing that works
There is currently a bug about this.
Below is a work around in order to get the following route types work
api/{controller}/ //Get All
api/{controller}/{Id} //Get for id
api/{controller}/{Id}/{Action}/ //Get all for action for controller with Id
you need to do the following.
Change your routing over to. (Note the default action..)
config.Routes.MapHttpRoute(
name : "DefaultAPi",
routeTemplate : "api/{controller}/{id}/{action}",
defaults: new
{
id = RouteParameter.Optional,
action = "DefaultAction"
}
);
In your controller change the base methods over to
[ActionName("DefaultAction")]
public string Get()
{
}
[ActionName("DefaultAction")]
public string Get(int id)
{
}
[ActionName("SpaceTypes")]
public string GetSpaceTypes(int id)
{
}
Now everything should work as expected..
Thanks to Kip Streithorst full this, for a full explanation
The way routing works in Web API is:
First it matches the URI against route template. At this stage, it's not looking at your controller actions
Then it looks for a matching controller
Then it looks for a method where (a) the action matches (POST in this case) and (b) every simple parameter type is matched with a value from the URI.
If there is a complex parameter type, it tries to read that from the request body.
By default, Web API tries to bind "simple" parameter types (like int) from the URI, and tries to read complex types from the request body.
See here for details: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection

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