This question already has an answer here:
WebAPI - Attribute Routing POST not working with WebAPI Cors?
(1 answer)
Closed 8 years ago.
I'm building an API using WEB API 2.
I have the following API controller:
[RoutePrefix("api/account")]
public class AccountController : ApiController
{
[Route("login")]
[HttpPost]
public IHttpActionResult AuthenticateUser(string username, string password)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
return BadRequest("You must submit username and password");
}
if (!Membership.ValidateUser(username, password))
{
return BadRequest("Incorrect username or password");
}
FormsAuthentication.SetAuthCookie(username, true);
return Ok();
}
}
And jquery function:
<script>
$(document).ready(function () {
$("#login-form").submit(function (e) {
e.preventDefault();
var username = $('#username').val();
var password = $('#password').val();
$.ajax({
type: 'POST',
url: '/api/account/Login/',
data: { username: username, password: password },
success: function() {
location.reload();
}
});
});
});
</script>
When I submit the login-form, I get the following error in Google Chrome's console:
POST http://localhost:60898/api/account/Login/ 404 (Not Found)
How can I create a route that accepts HTTP POST?
Thanks!
I'm sorry, I didn't see this post: WebAPI - Attribute Routing POST not working with WebAPI Cors?
I've updated my API controller like this:
[RoutePrefix("api/account")]
public class AccountController : ApiController
{
public class LoginInfo
{
public string username { get; set; }
public string password { get; set; }
}
[Route("login")]
[HttpPost]
public IHttpActionResult AuthenticateUser(LoginInfo loginInfo)
{
if (string.IsNullOrEmpty(loginInfo.username) || string.IsNullOrEmpty(loginInfo.password))
{
return BadRequest("You must submit username and password");
}
if (!Membership.ValidateUser(loginInfo.username, loginInfo.password))
{
return BadRequest("Incorrect username or password");
}
FormsAuthentication.SetAuthCookie(loginInfo.username, true);
return Ok();
}
}
And everything works fine now.
Related
const [payload, setPayload] = useState({
Email: null,
Password: null
});
const handleSubmit = async (e) => {
e.preventDefault();
var json = JSON.stringify(payload);
try {
const { data } = await axios.post(
"https://localhost:5001/api/User/Login",
{
json
}
);
console.log(data);
ctxDispatch({ type: "USER_LOGIN", payload: data });
localStorage.setItem("userInfo", JSON.stringify(data));
toast.success("Login Successful");
navigate("/");
} catch (err) {
toast.error(getError(err));
}
};
[AllowAnonymous]
[HttpPost("Login")]
public async Task<ActionResult<UserDTO>> Login([FromBody] LoginDTO loginDTO)
{
//LoginDTO loginDTO = Newtonsoft.Json.JsonConvert.DeserializeObject<LoginDTO>(input);
var pwd = Encrypt.ConvertToEncrypt(loginDTO.Password);
User currentUser = await _context.user.FirstOrDefaultAsync(user =>
user.Email.ToLower() == loginDTO.Email.ToLower()
&&`enter code here`
user.Password == pwd);
}
public class LoginDTO
{
public string Email { get; set; }
public string Password { get; set; }
}
When I post this string to my backend it attempts to parse my string but fails at the first char {. When i send this string wrapped in quotes through swagger it passes with no issues. When i send this string through postman with no quotes wrapped around the string I get the same exact error as i get when posting through my frontend.
fix the action input parameters, you don't need to deserialize it , it will be deserialized by MVC
public async Task< ....([FromBody] LoginDTO loginDto)
and since you are sending a json string, add content type to axios
axios.post('..your url', json, {
headers: {
// Overwrite Axios's automatically set Content-Type
'Content-Type': 'application/json'
}
});
I have configured my application to send a confirmation Email to the user after registration. After the registration is completed the user will see a page that is saying you need to confirm you Email:
<div *ngIf="!emailConfirmed">
<p> Please activate your account by clicking the relevant link in your Email </p>
</div>
<div *ngIf="emailConfirmed">
<p>
Your Email is now confirmed, please click the below button to Log-In.
</p>
<a class="btn btn-md btn-success btn-color" [routerLink]="['/login']">
Sign In
</a>
</div>
And emailConfirmed is just a simple variable that I have defined in the emailConfirmed's typescript relevant file:
export class EmailConfirmed {
public emailConfirmed: boolean;
}
After the user clicks on the link in his/her Email, his/her account will be verified and then the application will be redirected to the ConfirmEmail page again using the below code:
[HttpGet]
[Route("ConfirmEmail", Name = "ConfirmEmailRoute")]
public async Task<IActionResult> ConfirmEmail(string userId = "", string code = "")
{
//....
IdentityResult result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return Redirect("http://localhost:5000/emailconfirmed");
}
}
Now the question is: I don't know how can I set the emailConfirmed variable of EmailConfirmed component to true from WEB API and in the return Redirect line, in order the user see the second message this time? Also I doubt that I have chosen the best way to redirect the application to an Angular route using the return Redirect("http://localhost:5000/emailconfirmed"); line.
#ManojChoudhari is right. You can't route like this!
First it should be a "HttpPost". Return a response and then redirect on the clientside using router.
Here is a little example. This does not take into account the separation of concerns!
Serverside
Models
public class UserRequest {
public string UserId { get; set; }
public string Code { get; set; }
}
public class EMailConfirmationResponse {
public boolean EmailConfirmed { get; set; }
}
Controller
...
[HttpPost]
[Route("ConfirmEmail", Name = "ConfirmEmailRoute")]
public async Task<IHttpActionResult> ConfirmEmail(UserRequest user)
{
var result = await UserManager.ConfirmEmailAsync(user.UserId, user.Code)
if (result.Succeeded)
{
return Ok(new EMailConfirmationResponse { EmailConfirmed = true });
}
else
{
return BadRequest("An error occurred confirming the given email-address.");
}
}
...
Clientside
import { Component } from "#angular/core";
import { Router } from "#angular/router";
import { HttpClient } from "#angular/common/http";
#Component({
selector: "your",
templateUrl: "./your.component.html"
})
export class YourAngularComponent {
constructor(
private _router: Router,
private _http: Http
) {
...
// put this into your method
const httpOptions = { headers: new HttpHeaders({'Content-Type': 'application/json', 'Authorization': 'my-auth-token'}) };
this.http
.post("webapiurl", { userId: "TheUserId", code: "TheUserCode" }, httpOptions)
.subscribe((response) => {
const emailConfirmationResponse = response.json();
if(emailConfirmationResponse.emailConfirmed) {
this._router.navigate(["emailconfirmed"]);
}
}, (err: HttpErrorResponse) => {
// do some error handling here:
console.log(err.error);
console.log(err.name);
console.log(err.message);
console.log(err.status);
}
);
...
One thing you need to understand is - the Angular routes are only available on client side.
You will not be able to redirect user to angular template from server side.
The option you have probably is to return some flag from web API. This flag should be unique and then angular should redirect user to other page.
Your API Code should be:
[HttpGet]
[Route("ConfirmEmail", Name = "ConfirmEmailRoute")]
public async Task<IActionResult> ConfirmEmail(string userId = "", string code = "")
{
//....
IdentityResult result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return Ok();
}
else
{
return BadRequest("An error occurred confirming the given email-address.");
}
}
In your client side typescript you can add below code:
//// result = call web api and take result
//// if result is 200 OK then
this.router.navigate(['/your-path'])
Hope this helps.
I guess directly from webapi you can't redirect. use Api response and then in angular redirect to another page or domain.
The intention of the code:
The intention of this code is to accept data passed to it through the scope and place in an array to be passed to a Login controller (Server Side) via AJAX.
The Problem
The AJAX code does not seem as if it is successfully contacting the serverside action.
I have tried moving the action from the login controller to the home controller: No success.
I have tried following the guidelines of those who previously ran into this issue: No success.
I have tried hardcoding the URL to the action: No success.
Below is the AJAX Call:
login: function (username, password) {
var cred = { "uname": username, "pass": password };
var response = $http({
method: "POST",
url: '#(Url.Action("CheckUser","Login"))',
data: JSON.stringify({ model: cred }),
contentType: 'application/json; charset=utf-8',
dataType: "JSON",
success: function (msg) {
if (msg) {
console.log("success: " + msg);
}
},
error: function (msg) {
if (msg) {
console.log("Error:" + msg);
}
},
failure: function (msg) {
if (msg) {
console.log("fail: " + msg);
}
}
});
Next the action code:
namespace test4.Controllers
{
public class LoginController : Controller
{
// GET: Login
public ActionResult Login()
{
return View();
}
public class UserCred
{
public string Uname { get; set; }
public string Pass { get; set; }
}
[HttpPost]
public ActionResult CheckUser(UserCred umodel)
{
Console.WriteLine("I am here");
if (!ModelState.IsValid)
{
return Content("0");
}
string uname = umodel.Uname;
string pword = umodel.Pass;
using (localtestEntities entity = new localtestEntities()) {
var user = entity.users.Where(u => u.uname == uname).FirstOrDefault();
if (user != null) {
if (pword == user.pw) {
Session["LoginID"] = user.id;
Session["Username"] = user.fname + ' ' + user.lname;
return Content(user.id.ToString());
} else {
return Content("0");
}
} else {
return Content("0");
}
}
/* Begin Assigning values */
} //End Check User
}
}
The intended result is to pass back to the client side code whether the comparison of strings passed and what is in the database matches.
A couple things:
$Http({... is not an actual ajax function. I would recommend using $.post() for [HttpPost] and $.get() for [HttpGet]. You can review this in detail from jQuery's documentation here: https://api.jquery.com/jquery.post/
That URL will not work. You can't access razor helper methods inside of JS. As is, you're literally passing #(Url.Action("CheckUser","Login")) into your url so it would look something like localhost:3000/#(Url.Action("CheckUser","Login")) which obviously is not a legit route. You're going to have to get that URL to your ajax function a different way. Some options:
1) In the header of your .cshtml file, do something like:
<script>
$(() => {
const url = `#Url.Action("CheckUser","Login")`;
//Pass that into your JS code via a constructor parameter if your JS
//is a class or by function parameter if your JS code is a function.
const myJSClassNameHere = new myJSClassHere(url);
});
</script>
2) you can just hard code the url into the ajax call. This is a viable option if you don't anticipate the url to change and/or if your standards allow it.
I'm trying out AngularJS in combination with NET Core. Currently trying to parse a "string username", "string password" and "bool rememberMe", but both strings are null no matter what I do. I've even tried parsing a constant value as seen below, the parameter "test", but even that doesn't work. I've confirmed in Fiddler that the script is in fact parsing the value, so the controller basically just can't "fetch it".
Below is my AngularJS loginController code.
var login = angular.module('login', []);
login.controller('LoginController',
[
'$scope', '$http', '$window', function ($scope, $http, $window) {
$scope.submit = function () {
var data = { test: ':D' };
$http({
method: 'POST',
url: '/admin',
data: data
}).then(function success(response) {
alert(response.data.test);
});
}
}
]);
Below is the code for my ActionResult meant to fetch the value being posted to it.
[HttpPost]
public IActionResult Index(string test)
{
return Json(new
{
test,
bla = "BLA"
});
//if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
//{
// if (UserSystem.Login(username, password, rememberMe, HttpContext))
// {
// HttpContext.Response.StatusCode = (int)HttpStatusCode.Accepted;
// return Json(new
// {
// returnUrl = "/admin/dashboard"
// });
// }
//}
//HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
//return Json(new
//{
// error = "Username and/or password do not match"
//});
}
I've also tried specifying the Content-Type, but also without luck. Either way, in Fiddler the request always seems to per default be application/json.
I hope someone can help me. Thanks!
The solution was to request an object in the controller, rather than "string username", "string password" and "bool rememberMe".
The controller now looks like this:
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
public bool RememberMe { get; set; }
}
[HttpPost]
public IActionResult Index([FromBody]LoginModel model)
{
if (!string.IsNullOrEmpty(model.Username) && !string.IsNullOrEmpty(model.Password))
{
if (UserSystem.Login(model.Username, model.Password, model.RememberMe, HttpContext))
{
HttpContext.Response.StatusCode = (int)HttpStatusCode.Accepted;
return Json(new
{
returnUrl = "/admin/dashboard"
});
}
}
HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return Json(new
{
error = "Username and/or password do not match"
});
}
And the AngularJS controller now looks like this:
var login = angular.module('login', []);
login.controller('LoginController',
[
'$scope', '$http', '$window', function ($scope, $http, $window) {
$scope.submit = function () {
var data = {
username: $scope.username,
password: $scope.password,
rememberMe: $scope.rememberMe
};
$http({
method: 'POST',
url: '/admin',
data: data
}).then(function success(response) {
$window.location.href = response.data.returnUrl;
}, function fail(response) {
swal('Error!', response.data.error, 'error');
});
}
}
]);
I have been trying to pass 3 parameters into my post method from angularjs to my C# web api controller. The information is being passed to the angularjs service correctly. But Have tried many combinations on the client and server side post method to receive multiple parameters but the closest I have come is 2/3.
here is my code.
SERVER SIDE CONTROLLER:
// POST api/values
[HttpPost("{id}")]
public IActionResult Post(int id, string userId, [FromBody]Post post)
{
if(post.Id == 0)
{
_repo.AddPost(id, userId, post);
}
else
{
_repo.UpdatePost(post);
}
return Ok(post);
}
CLIENT SIDE SERVICE:
namespace MyApp.Services {
export class PostsService {
private postsResource;
constructor(private $resource: ng.resource.IResourceService) {
this.postsResource = this.$resource("/api/posts/:id");
}
getPost(id) {
return this.postsResource.get({ id: id });
}
savePost(id, userId, postToSave) {
return this.postsResource.save({ id: id }, { userId: userId }, postToSave).$promise;
}
deletePost(id) {
return this.postsResource.delete({ id: id }).$promise;
}
}
angular.module("MyApp").service("postsService", PostsService);
}
Any tips or suggestions? Much appreciated!