Simple POST with ASP.NET Framework - c#

Here is my problem, I want to receive the Username and Password of the client with POST request.
The code seems simple but doesn't work
The LoginController.cs :
public class LoginController : ApiController
{
[HttpPost]
[ActionName("Login")]
[Route("api/{controller}")]
public HttpResponseMessage Login([FromBody] LoginJson json)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
}
The LoginJson.cs form :
public class LoginJson
{
public string Username { get; set; }
public string Password { get; set; }
}
The ajax request with jQuery, i don't want to change the url because i want to use 3 urls /api/Login, /api/Method1 and /api/Method2 for 3 different controllers:
$.ajax({
url: '/api/Login',
type: 'POST',
dataType: "json",
contentType: "application/json, charset=utf-8",
data: JSON.stringify({
Username: username,
Password: password,
}),
...
});
The route for API, in Global.asax.cs :
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalFilters.Filters.Add(new HandleErrorAttribute());
RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
RouteTable.Routes.MapRoute(...)
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}",
defaults: new {action = RouteParameter.Optional}
);
}
I get error 404 (Not Found).
I will change the Global.asax.cs file.

You are only calling the controller with no action and since there is no default action defined you'll get a 404 error.
In the jQuery you can do:
url: '/api/Login/Login'
Or change the routing by either putting this tag:
[Route("api/login")]
Or in the RouteConfig.cs, this should be done before the other routes are set including the generic one.
routes.MapRoute("Login", "Login/{action}",
defaults: new { controller = "Login", action = "Login" });

There is no api/login
You can do like that:
[ActionName("api/login")]
public HttpResponseMessage Login([FromBody] LoginJson json)
{
return Request.CreateResponse(HttpStatusCode.OK);
}

Related

Asp.net core mvc ajax post to controller with multiple parameters return bad request

I have my controller action methods as follows,
public class UserController : BaseController<UserController>
{
[HttpGet]
public IActionResult Assignment(Guid id)
{
return View();
}
[HttpPost]
public IActionResult Assignment(Guid id, [FromBody] List<UserViewModel> assignees)
{
return View();
}
}
The ajax method in Assignment.cshtml page
$("#btn-save").click(function () {
var url = "/User/Assignment/#Model.SelectedUser.Id";
$.ajax({
type: "POST",
url: url,
contentType: "application/json",
data: JSON.stringify({ assignees: assignmentPage.SelectedUsers })
});
});
So this builds a url like;
http://localhost:8800/User/Assignment/f474fd0c-69cf-47eb-7281-08d6536da99f
This is the only route configuration in my Startup.cs.
app.UseMvc(routes =>
{
routes.MapRoute(name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
I never hit to Assignment post action, server returns 400, I've searched and couldn't find yet, how am I supposed to configure my route for an action like this?
The data you are sending to your server is invalid. It's expecting a JSON array, but you're sending it a JSON object with a single property that itself is an array. You receive a 400 status-code due to the fact that JSON.NET cannot parse an object as an array.
For further clarification, this is what you're sending:
{
"assignees": [
{ ... assignee1 ... },
{ ... assignee2 ... },
...
]
}
However, it's the array that's expected, so it should look like this:
[
{ ... assignee1 ... },
{ ... assignee2 ... },
...
]
All you need to do is change your current JSON.stringify line to this:
JSON.stringify(assignmentPage.SelectedUsers)
An alternative option is to create a model class in your ASP.NET Core project and use that instead of a list of strings. Here's what that would look like:
public class AssignmentModel
{
public List<UserViewModel> Assignees { get; set; }
}
The Assignment action would look like this:
public IActionResult Assignment(Guid id, [FromBody] AssignmentModel thisNameDoesNotMatter)
The problem for my case is I am using AutoValidateAntiforgeryTokenAttribute configuration
services.AddMvc(options =>{
// Automatically add antiforgery token valdaiton to all post actions.
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
}
So, ajax post has not any request verification token with itself, lack of this token server returns bad request.
To overcome this, I followed this link, so my final working code as,
Assignment.cshtml
#inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
#functions
{
public string GetAntiXsrfRequestToken() => Xsrf.GetAndStoreTokens(Context).RequestToken;
}
<input type="hidden" id="RequestVerificationToken" name="RequestVerificationToken"
value="#GetAntiXsrfRequestToken()">
<script type="text/javascript">
$("#btn-saveinspectors").click(function () {
var url = "/Audit/Assignment/#Model.SelectedUser.Id";
var assignees = JSON.stringify(assignmentPage.SelectedUsers);
$.ajax({
type: "POST",
url: url,
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN", $('#RequestVerificationToken').val());
},
contentType: "application/json",
data: assignees,
});
});
</script>
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
}
Note: This is my case that there is not any form inside my page where a request verification token is generated automatically. If you have a form in your page where you make an ajax request then you can follow this post

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 }
);

How I can parse a json sended using ajax (POST) to NameValueCollection Class?

Hollo:
I'm sending the following message to mi asp.net web api
var user ={
Username: "user",
Password: "pass"
};
$.ajax({
url: 'http://WebApiDir',
type: 'POST',
contentType: "application/json",
data: JSON.stringify(user),
success: function (data) {
},
});
I Caught the request on a DelegatingHandler.
My question is How to parse the message included on the HttpContent to a NameValueCollection class
I try do the following
var sQuery = await request.Content.ReadAsFormDataAsync().Result;
but this produce a exception becase the Result attr is null.
Thanks for your answers
This is something I do:-
$.ajax
({
url: 'Default.aspx/MyMethod',
type: 'POST',
dataType: "json",
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ ID: ID }),
success: onSuccess,
fail: onFail
});
and then on the C# side:-
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
// MyMethod is used to pass Identifcation number from client side to server side via Ajax.
public static string MyMethod(string ID)
{
........ do whatever needs to be done ........
return string.Format("Thanks for calling me with Ajax, the ID: " + data);
}
If you are indeed using WebAPI, you should allow the framework to do the heavy lifting for you.
First, you need to make sure that your routing is set up correctly and that you are actually calling the route from your javascript. In your case, App_Start/WebApiConfig.cs would look something like:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Then, give your api controller a meaningful name name such as Users and define the method as a post, since you are posting user info, taking User as a parameter
public class User
{
public string Username { get; set; }
public string Password { get; set; }
}
public string Post(User user)
{
// perform actions on user data
return "success";
}
Finally, you need to properly define the route in your javascript call.
var user ={
Username: "user",
Password: "pass"
};
$.ajax({
url: 'http://WebApiDir.com/api/Users',
type: 'POST',
contentType: "application/json",
data: user,
success: function (data){},
});

Categories