Angular post request to c# web api 2 RESTful web service - c#

I'm trying to send a http post request from angular client to C# web API 2 RESTful web service.
My client:
var userId = "123456";
var password = "654321";
const headersContent = new Headers().set('Content-Type', 'application/x-www-form-urlencoded');
var url = "http://localhost:35615/login"
this.http.post(url, {
"userId": userId,
"password": password
}, {withCredentials: true}).subscribe(res => {
console.log(res);
});
My Server:
[Route("login")]
[HttpPost]
public IHttpActionResult LoginReq([FromBody] string parameters)
{
//Deserialize the parameters.
}
My problem is the parameters var is null although the post request in the network tab in chrome includes the data.
Can someone explain me what I'm doing wrong and how can I fix it?
Thank you!

You are passing an anonymous object with properties "UserId" and "Password".
Make a Data Contract class which has those 2 properties as strings and use it in the parameters of your REST method.
public IHttpActionResult LoginReq([FromBody] User user) { ... }

If you are passing an object from your Angular POST request the Web API POST method can be changed to accept an User defined type as parameter to read it from the request body.
You can create the below user defined type in C# to bind UserId and Password properties from your angular Post request
public class UserLogin
{
public int UserId { get; set; }
public string Password { get; set; }
}
[Route("login")]
[HttpPost]
public IHttpActionResult LoginReq([FromBody] UserLogin user)
{
//Deserialize the parameters.
}
I would recommend going through this documentation to read more about parameter binding in Web API. Believe me its worth your time.

You post userId and password, but expect String parameters. Change to String userId, String password. The Modelbinder only will bind matching properties.

Just add JSON.stringify() you are sending an object to the server which expects only a string as a parameter so make it as a string and pass the value otherwise create a model with userid and password in your server side and mention that object
let obj = {
"userId": userId,
"password": password
};
this.http.post(url, JSON.stringify(obj), {withCredentials: true}).subscribe(res => {
console.log(res);
});
The above code will work with the string parameters - Going forward try to use model and pass the object from Angular
Happy Coding !!

Related

Model binding not working in aspnet core web api

I am working on ASP.NET Core 2.1 Web API project. I am trying to follow this article: https://www.c-sharpcorner.com/article/jwt-json-web-token-authentication-in-asp-net-core/ but I am stuck at Action. My model class just won't bind to the input.
[AllowAnonymous]
[HttpPost]
public IActionResult Login([FromBody] LoginVM loginVM)
{
IActionResult response = Unauthorized(); // cant reach this point, my breakpoint is here
var user = AuthenticateUser(new UserModel { });
if (user != null)
{
var tokenString = GenerateJSONWebToken(user);
response = Ok(new { token = tokenString });
}
return response;
}
public class LoginVM
{
public string Username { get; set; }
public string Password { get; set; }
}
You're posting as x-www-form-urlencoded, but you have the [FromBody] attribute applied to the action param. These two things are fundamentally incompatible. To accept x-www-form-urlencoded (or multipart/form-data) you must apply the [FromForm] attribute to the param. If you have [FromBody], as you do now, then you can only accept something like application/json or application/xml (if you also enable the XML serializers).
If the issue is that you want to be able to accept both application/json and x-www-form-urlencoded request bodies, that is not possible. You'll need a separate action for each request body encoding, though you can factor out the actual meat of the action into a private method on the controller that both actions can utilize.
Choose "raw" in Body and "Content-Type" as "application/json" in postman and then try.

Passing JSON parameter via GET request to MVC controller

I'm trying to pass some JSON on the querystring of a GET request to an MVC controller, but can't seem to get it to come through as anything other than null.
Ajax (through TypeScript)
$.ajax(url, {
method: 'GET',
data: { 'request': JSON.stringify(this.request) },
dataType: 'json'
})
MVC Controller
[Route("stuffAndThings/{request?}")]
public async Task<HttpResponseMessage> GetStuff(requestType request)
{
}
Because this is TypeScript, the object being passed is the TypeScript representation of the C# model, including several custom objects
TS class
class requestType {
pageData: PageData;
}
C# class
public class requestType
{
public PageData pageData { get; set; } = new PageData();
}
Looking at the request in devtools, it appears to be being passed correctly on the querystring, but always comes through on the controller as null.
What am I missing?
EDIT
To address a couple of comments, the controller method is purely for data retrieval, and does have the potential in the future to be turned into a WebAPI method, so I would like to keep it as a GET request if possible.
In MVC controller you will get the parameter as string because you have passed parameters as string through GET request
[Route("stuffAndThings/{request?}")]
public async Task<HttpResponseMessage> GetStuff(string request)
{
}
Make requestType class serializable,
now in your method you have to deserialize the json string into your object
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(request)))
{
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(requestType));
requestType requestObj = (requestType)deserializer.ReadObject(ms);
//your code here
}
Json.Stringfy convert your request in string form, and in controller your are fetching with particular type. So, to get the proper result update with strin g instead of RequestType.
if still face issue you reach out to me.
I am here
Please response or like if its helpful.

asp.net core Api - insert new row in database

I'm trying to create an address object in a database through an asp.net core api. I'm using Postman to invoke the method.
Class:
namespace LC.Tools.API.Controllers {
[Route("api/[controller]")]
public class MailerController : Controller
{
Data.LCToolsDbContext _context;
public MailerController(Data.LCToolsDbContext context)
{
_context = context;
}
[HttpPost]
public async Task<IActionResult> CreateAddress(int client, string name, string email)
{
Address adr = new Address() { ClientId = client, Name = name, Email = email };
_context.MailerAddresses.Add(adr);
await _context.SaveChangesAsync();
return Ok(adr);
}
} }
URL (using POST):
http://localhost:50444/api/mailer/createAddress?client=1&name=test&email=mail#mail.no
I also have a break point in the method, but it never hits. I don't get any error message, it just doesn't do anything.
You can see #Rick van den Bosch's comment but still you would like to specify the route with action. use this
[Route("api/[controller]/[action]")]
See #Ayvaras' comment. Since you're building a Web API controller, you don't have to specify the action name in your URL. The HttpPost points all POST actions to the mailer controller towards the CreateAddress method.
Your POST URL should be:
http://localhost:50444/api/mailer?client=1&name=test&email=mail#mail.no
Problem solved! Thanks Ayvaras,
[Route("api/[controller]/[action]")]
did the trick. Now that the method is found I can look into how to pass an object instead of using querystring
You are not using POST correctly.
If you use POST you should not be sending parameters through the query string, you should use the request body. Even though, if you still have to send them via query string you should use the FromQuery attribute in your action parameters.
[HttpPost]
public async Task<IActionResult> CreateAddress([FromQuery] int client, [FromQuery] string name, [FromQuery] string email)
{
Address adr = new Address() { ClientId = client, Name = name, Email = email };
_context.MailerAddresses.Add(adr);
await _context.SaveChangesAsync();
return Ok(adr);
}
Edit: Use [Route("api/[controller]/[action]")] to append the action name in your action route.
Instead of changing the controllers routing scheme, it's also possible to specify the endpoint name by doing the following
[HttpPost("createAddress")]
public async Task<IActionResult> CreateAddress(int client, string name, string email)
{
[...]
}

How to fix - The requested resource does not support http method 'POST'

Below is WebAPI action. On googling about the below error:-
The requested resource does not support http method 'POST'
I got number of links & updated my api accordingly but still I am getting the same error.
Web api not supporting POST method
ASP.NET Web Api: The requested resource does not support http method 'GET'
[AcceptVerbs("POST")]
[HttpPost]
[Route("rename/{userId}/{type}/{title}/")]
public IHttpActionResult Rename([FromBody] int userId, [FromBody] string type, [FromBody] string title)
{
//my api stuff
}
But still when calling the above via post man throws the error.
How do I get rid of this error??
Also is it possible to fix this without using [FromBody] attribute in the method parameters list?
Any help/suggestion highly appreciated.
Thanks.
You have declared route which requires url parameters
[Route("rename/{userId}/{type}/{title}/")]
So when you send request to api/customer/rename it does not match this method. You should remove parameters which you are passing in request body from route parameters
[Route("rename")]
Make sure that you have appropriate RoutePrefix("api/customer") attribute on your controller.
Second problem is multiple [FromBody] parameters. You will get can't bind multiple parameters error. There is limitation - you can mark only one parameter as FromBody. See Sending Simple Types notes:
Web API reads the request body at most once, so only one parameter of
an action can come from the request body. If you need to get multiple
values from the request body, define a complex type.
You should create complex type which will hold all parameters
public class RenameModel
{
public int UserId { get; set; }
public string Type { get; set; }
public string Title { get; set; }
}
And change method signature to
[HttpPost]
[Route("rename")]
public IHttpActionResult Rename(RenameModel model)
And send request data as application/x-www-form-urlencoded
[Route("rename/{userId}/{type}/{title}/")]
public IHttpActionResult Rename([FromBody] int userId, [FromBody] string type, [FromBody] string title)
The last answer is correct, you're asking for these parameters in the route, but saying that you expect them in the post body. Also, usually the route would begin with a noun rather than a verb. What is it you're renaming? (i.e. [Route("users/rename/{userId}/{type}/{title}")]
Based on your initial post, try this instead:
[HttpPost]
[Route("rename/{userId}/{type}/{title}" Name = "RenameUser"]
public IHttpActionResult Rename(int userId, string type, string title)
{
_myServiceMethod.Rename(userId, type, title);
return new StatusCodeResult(HttpStatusCode.Created, this);
}
Or, if you wanted to do a post with the info in the body:
Declare your data contract:
public class User
{
public string Type { get; set; }
public string Title { get; set; }
}
Then on the endpoint:
[HttpPost]
[Route("rename/{userId}", Name = "RenameUserPost")]
public IHttpActionResult RenameUserPost(int userId, [FromBody] User userData)
{
return new StatusCodeResult(HttpStatusCode.Created, this);
}
Note that in both returns 'this' refers to your controller class that inherits from ApiController. Verified both of these in swagger, and they accept POSTs and return status codes.
Hope this helps.
I had this error for wrong string in Route string on top of my action.
[Route("api/TestReaderPercentStudyHomework/AddOrUpdate")]

How to implement Request / Response pattern with Web Api?

I'm new to MVC Web Api but I have some experience working with ServiceStack framework. Some of the web api examples look a lot like RPC with more action methods and less parameters. In fact most examples seem to limit the request parameter to an id. I would like to create some reporting services using request/response because the request gets quite complex with all the reporting criteria.
Here's my simplified request / response types:
public class PendingRequest
{
public string Id { get; set; }
public int AccountId { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
}
public class PendingResponse
{
public string Id { get; set; }
public IEnumerable<Pending> Data { get; set; }
}
And my outline reports controller:
public class ReportsController : ApiController
{
public async Task<PendingResponse> GetPending(PendingRequest request)
{
return new PendingResponse
{
Id = request.Id,
// Data = await Repo.GetPending()
};
}
public async Task<ShippedResponse> GetShipped(ShippedRequest request)
{
return new ShippedResponse
{
Id = request.Id,
// Data = await Repo.GetShipped()
};
}
public async Task<ProductsResponse> GetProducts(ProductsRequest request)
{
return new ProductsResponse
{
Id = request.Id,
// Data = await Repo.GetProducts()
};
}
}
And my routing and config for a self-hosting app:
class Program
{
static void Main()
{
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute(
name: "Reports",
routeTemplate: "api/reports/{action}",
defaults: new
{
controller = "Reports"
});
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
}
}
So with this routing I intend the action to be the name of the report. Note i've not included the request parameter here. Not sure if I should or not.
The problem is actually on the client side. With the HttpClient the GetAsync method does not allow me to include the JSON request object. I see an extension method PostAsJsonAsync but surely this shouldn't be a POST? Without a GetAsJsonAsync or similar then I can't see a way of including a request with a GET?
To be honest I prefer ServiceStack but there appears to be no async support yet. I need to develop some high performance services which are I/O intensive and I want to reduce the blocking as much as possible.
UPDATE:
It seems Web Api will perform the model binding if, rather than using the request body, I include the model's parameters as part of the query string. In order for this to work, the Get method needs to prefix the model type with the [FromUri] attribute, which informs the model binder that the model should be constructed from the request query string. Messy, but works.
[ActionName("Pending")]
public async Task<PendingResponse> GetPending([FromUri] PendingRequest request)
{
return new PendingResponse
{
Id = request.Id,
// query = ...build from request params
// Data = await Repo.GetPending(query)
};
}
And now on the client side I perform the following:
var result = await _httpClient.GetAsync("api/reports/pending?Id=123&AccountId=456");
result.EnsureSuccessStatusCode();
var response = await result.Content.ReadAsAsync<PendingResponse>();
A call to the service results in the GetPending method being called with a copy of the PendingRequest object materialised from the supplied query string parameters.
The fundamental problem here is you are trying to provide a request body to HTTP GET, which is not allowed. Well, you can still go ahead and format a GET with request body and submit it but that is not according to the spec. Strictly speaking, HTTP spec does not forbid GET requests from having a body but the response for a GET request must not change based on the request body, which basically means you cannot use search criteria in the GET request body. You can use the URI path and query string to specify the search criteria and if you want to bind them into a complex type parameter, you will need to use [FromUri] like this: public async Task<PendingResponse> GetPending([FromUri]PendingRequest request).
Bit of a guess, but what about just URL encoding the JSON request object and putting it in the query string segment of the GET URI?
Also, you might need [FromUri] attribute on your request method parameter in the action as it is a complex type. I may have missed something, but this article on parameter binding doesn't mention anything about GET being a special case for complex types, hence I think they will always try and read from the POST body.

Categories

Resources