On my Create page I am using ajax and calling my api controller when creating a person:
<script>
$(document).ready(function() {
var newUrl = '#Url.Action("Index", "PersonInformations")';
var settings = {};
settings.baseUri = '#Request.ApplicationPath';
var infoGetUrl = "";
if (settings.baseUri === "/ProjectNameOnServer") {
infoGetUrl = settings.baseUri + "/api/personinformations/";
} else {
infoGetUrl = settings.baseUri + "api/personinformations/";
}
$("#Create-Btn").on("click",
function(e) {
$("form").validate({
submitHandler: function () {
e.preventDefault();
$.ajax({
method: "POST",
url: infoGetUrl,
data: $("form").serialize(),
success: function () {
toastr.options = {
onHidden: function () {
window.location.href = newUrl;
},
timeOut: 3000
}
toastr.success("Individual successfully created.");
},
error: function (jqXHR, textStatus, errorThrown) {
var status = capitalizeFirstLetter(textStatus);
var error = $.parseJSON(jqXHR.responseText);
//console.log(jqXHR.responseText);
toastr.error(status + " - " + error.message);
}
});
}
});
});
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
});
</script>
Here is the method in my PersonInformations API controller:
[ResponseType(typeof(PersonInformation))]
public IHttpActionResult PostPersonInformation(PersonInformation personInformation)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var lstOfPersons = db.PersonInformations.Where(x => x.deleted == false).ToList();
if (lstOfPersons.Any(
x =>
x.FirstName == personInformation.FirstName && x.LastName == personInformation.LastName &&
x.AId == personInformation.AgencyId && x.ID != personInformation.ID))
{
ModelState.AddModelError("", "This person already exists!");
return BadRequest(ModelState);
}
if (
lstOfPersons.Any(
x => x.Email.ToLower() == personInformation.Email.ToLower() && x.ID != personInformation.ID))
{
ModelState.AddModelError(personInformation.Email, "This email already exists!");
return BadRequest(ModelState);
}
personInformation.FirstName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(personInformation.FirstName);
personInformation.LastName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(personInformation.LastName);
db.PersonInformation.Add(personInformation);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = personInformation.ID }, personInformation);
}
Now when I test this and purposely enter an email that already exists, the ajax request errors out but returns the message:
Error - The Request is invalid
but when I use console.log(jqXHR.responseText)
I get this:
Create
{
"$id": "1",
"message": "The request is invalid.",
"modelState": {
"$id": "2",
"test#test.com": [
"This email already exists!"
]
}
}
How do I get the "This email already exists!" as the error message?
I just figured this out. I know that the modelState was an array type, and the 2nd value was the actual email address entered.
So I edited my button.click event to this:
$("#Create-Btn").on("click",
function(e) {
$("form").validate({
submitHandler: function () {
e.preventDefault();
var emailValue = $("#Email").val();
$.ajax({
method: "POST",
url: infoGetUrl,
data: $("form").serialize(),
success: function () {
toastr.options = {
onHidden: function () {
window.location.href = newUrl;
},
timeOut: 3000
}
toastr.success("Individual successfully created.");
},
error: function (jqXHR, textStatus, errorThrown) {
var status = capitalizeFirstLetter(textStatus);
var error = $.parseJSON(jqXHR.responseText);
toastr.error(status + " - " + error.modelState[emailValue]);
}
});
}
});
});
Find the error message via array bracket notation along with getting the actual value of the email trying to be submitted did the trick.
A public action method 'AddPromoCode' was not found on controller
'Flazingo.Controllers.PositionController'. at
System.Web.Mvc.Controller.HandleUnknownAction(String actionName) at
System.Web.Mvc.Controller.ExecuteCore() at
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at
System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.b__5()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.b__0()
at System.Web.Mvc.MvcHandler.<>c__DisplayClasse.b__d() at
System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step,
Boolean& completedSynchronously)
Here is the ajax call:
$.ajax({
url: '/Position/AddPromoCode',
type: 'POST',
dataType: "json",
contentType: "application/json; charset:utf-8",
data: ko.toJSON(viewModel),
success: function(result){
if(result.TypeId == 1){
viewModel.promoOff(viewModel.grandTotal() * (result.Value / 100));
viewModel.PromoCodes.push(promoCode + ": "+ result.Value + "% off");
}else{
viewModel.PromoCodes.push(promoCode + ": "+ result.Value + "days free");
}
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
var errorData = jQuery.parseJSON(xmlHttpRequest.responseText);
var errorMessages = [];
//this ugly loop is because List<> is serialized to an object instead of an array
for (var key in errorData)
{
errorMessages.push(errorData[key]);
}
toastr.error(errorMessages.join("<br />"), 'Uh oh');
}
});
EDIT: POST METHOD
[HttpPost]
public ActionResult AddPromoCode(PaymentViewModel model)
{
List<string> errors = new List<string>();
try
{
var position = db.Positions.SingleOrDefault(x => x.PositionId == model.PositionId);
if (position != null)
{
var promo = db.Promotions.SingleOrDefault(x => x.Code.ToLower() == model.PromoCode.ToLower() && x.IsUserEntered);
if (promo != null)
{
var promoUsage = db.PromoCodeUsages.SingleOrDefault(x => x.PromotionId == promo.PromotionId && x.ClientId == position.Client.Id);
int used = 0;
if (promoUsage != null)
{
used = promoUsage.Used;
}
if (used < promo.QuantityUsage)
{
if (DateTime.Today >= promo.StartDate && DateTime.Today <= promo.EndDate)
{
position.PromoCodes.Add(new PositionPromoCode
{
PromotionId = promo.PromotionId
});
var clientPC = position.Client.PromoCodes.SingleOrDefault(x => x.PromotionId == promo.PromotionId);
if (clientPC != null)
{
clientPC.Used = used + 1;
}
else
{
clientPC = new PromoCodeUsage()
{
PromotionId = promo.PromotionId,
Used = used + 1
};
position.Client.PromoCodes.Add(clientPC);
}
db.SaveChanges();
Response.StatusCode = (int)HttpStatusCode.OK;
return Json(new { Value = promo.Value, TypeId = promo.PromotionTypeId });
}
else
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
errors.Add("Sorry seems like this promotion code has expired");
db.SaveChanges();
return Json(errors);
}
}
else
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
errors.Add("Sorry seems like you have already used this code, or its not applicable anymore!");
db.SaveChanges();
return Json(errors);
}
}
else
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
errors.Add(string.Format("Sorry we don't have '{0}' promocode in our system!", model.PromoCode));
db.SaveChanges();
return Json(errors);
}
}
Response.StatusCode = (int)HttpStatusCode.BadRequest;
errors.Add("We coudn't find this position in our system!");
db.SaveChanges();
return Json(errors);
}
catch (Exception ex)
{
logger.Log(LogLevel.Error, string.Format("{0} \n {1}", ex.Message, ex.StackTrace));
Response.StatusCode = (int)HttpStatusCode.BadRequest;
errors.Add("Sorry there was internal errors, flazingo.com has been notified.");
return Json(errors);
}
}
JSON Structure:
{"PositionTitle":"Testing B Syntax Error","PromoCode":"FREECB","FirstName":null,"LastName":null,"Address":null,"SuiteNumber":null,"PhoneNumber":null,"City":null,"State":null,"ZipCode":null,"CreditCardNumber":null,"ExperationMonth":null,"ExperationYear":null,"CCV":null,"ClientId":2,"CustomerProfileId":64277420,"PositionId":78,"EmailAddress":"jmogera#gmail.com","Years":[{"Selected":false,"Text":"2013","Value":"2013"},{"Selected":false,"Text":"2014","Value":"2014"},{"Selected":false,"Text":"2015","Value":"2015"},{"Selected":false,"Text":"2016","Value":"2016"},{"Selected":false,"Text":"2017","Value":"2017"},{"Selected":false,"Text":"2018","Value":"2018"},{"Selected":false,"Text":"2019","Value":"2019"},{"Selected":false,"Text":"2020","Value":"2020"},{"Selected":false,"Text":"2021","Value":"2021"},{"Selected":false,"Text":"2022","Value":"2022"}],"MonthList":[{"Selected":false,"Text":"Jan","Value":"01"},{"Selected":false,"Text":"Feb","Value":"02"},{"Selected":false,"Text":"Mar","Value":"03"},{"Selected":false,"Text":"Apr","Value":"04"},{"Selected":false,"Text":"May","Value":"05"},{"Selected":false,"Text":"Jun","Value":"06"},{"Selected":false,"Text":"Jul","Value":"07"},{"Selected":false,"Text":"Aug","Value":"08"},{"Selected":false,"Text":"Sep","Value":"09"},{"Selected":false,"Text":"Oct","Value":"10"},{"Selected":false,"Text":"Nov","Value":"11"},{"Selected":false,"Text":"Dec","Value":"12"}],"IsAddingNewCard":false,"HaveCardOnFile":true,"AddOns":[{"PositionId":78,"ProductId":2,"Description":"The heart and soul, we take you through the hiring process, start to finish and give you every tool you need to make a great hire along the way.","Price":39,"HasAdded":true,"AutoRenew":true,"Name":"Complete Hiring System","AddOnId":122}],"CreditCards":[{"CreditCardId":16,"LastFour":"1060","HolderName":"Barrett Kuethen","ExpDate":"/Date(1422766800000)/","IsDefault":true}],"CardOnFile":{"CreditCardId":16,"LastFour":"1060","HolderName":"Barrett Kuethen","ExpDate":"/Date(1422766800000)/","IsDefault":true},"PromoCodes":[],"__ko_mapping__":{"CardOnFile":{},"ignore":[],"include":["_destroy"],"copy":[],"observe":[],"mappedProperties":{"PositionTitle":true,"PromoCode":true,"FirstName":true,"LastName":true,"Address":true,"SuiteNumber":true,"PhoneNumber":true,"City":true,"State":true,"ZipCode":true,"CreditCardNumber":true,"ExperationMonth":true,"ExperationYear":true,"CCV":true,"ClientId":true,"CustomerProfileId":true,"PositionId":true,"EmailAddress":true,"Years[0].Selected":true,"Years[0].Text":true,"Years[0].Value":true,"Years[1].Selected":true,"Years[1].Text":true,"Years[1].Value":true,"Years[2].Selected":true,"Years[2].Text":true,"Years[2].Value":true,"Years[3].Selected":true,"Years[3].Text":true,"Years[3].Value":true,"Years[4].Selected":true,"Years[4].Text":true,"Years[4].Value":true,"Years[5].Selected":true,"Years[5].Text":true,"Years[5].Value":true,"Years[6].Selected":true,"Years[6].Text":true,"Years[6].Value":true,"Years[7].Selected":true,"Years[7].Text":true,"Years[7].Value":true,"Years[8].Selected":true,"Years[8].Text":true,"Years[8].Value":true,"Years[9].Selected":true,"Years[9].Text":true,"Years[9].Value":true,"Years":true,"MonthList[0].Selected":true,"MonthList[0].Text":true,"MonthList[0].Value":true,"MonthList[1].Selected":true,"MonthList[1].Text":true,"MonthList[1].Value":true,"MonthList[2].Selected":true,"MonthList[2].Text":true,"MonthList[2].Value":true,"MonthList[3].Selected":true,"MonthList[3].Text":true,"MonthList[3].Value":true,"MonthList[4].Selected":true,"MonthList[4].Text":true,"MonthList[4].Value":true,"MonthList[5].Selected":true,"MonthList[5].Text":true,"MonthList[5].Value":true,"MonthList[6].Selected":true,"MonthList[6].Text":true,"MonthList[6].Value":true,"MonthList[7].Selected":true,"MonthList[7].Text":true,"MonthList[7].Value":true,"MonthList[8].Selected":true,"MonthList[8].Text":true,"MonthList[8].Value":true,"MonthList[9].Selected":true,"MonthList[9].Text":true,"MonthList[9].Value":true,"MonthList[10].Selected":true,"MonthList[10].Text":true,"MonthList[10].Value":true,"MonthList[11].Selected":true,"MonthList[11].Text":true,"MonthList[11].Value":true,"MonthList":true,"IsAddingNewCard":true,"HaveCardOnFile":true,"AddOns[0].PositionId":true,"AddOns[0].ProductId":true,"AddOns[0].Description":true,"AddOns[0].Price":true,"AddOns[0].HasAdded":true,"AddOns[0].AutoRenew":true,"AddOns[0].Name":true,"AddOns[0].AddOnId":true,"AddOns":true,"CreditCards[0].CreditCardId":true,"CreditCards[0].LastFour":true,"CreditCards[0].HolderName":true,"CreditCards[0].ExpDate":true,"CreditCards[0].IsDefault":true,"CreditCards":true,"CardOnFile":true,"PromoCodes":true},"copiedProperties":{}},"addNewCreditCardValidationGroup":{"FirstName":null,"LastName":null,"Address":null,"City":null,"State":null,"CreditCardNumber":null,"ExperationMonth":null,"ExperationYear":null,"CCV":null,"errors":[]},"promoOff":0,"grandTotal":39}
Note: the call is made within a knockout click function.
Note: I have created another issue, thinking it was client side issue. This is related.
Uncaught SyntaxError: Unexpected token B on live but not local server
It can't find AddPromoCode so, there seems a url problem. You should add the application name at ajax code in server like url: '/ApplicationName/Position/AddPromoCode'.
You can also do this with url.action html helper, like:
// In layout or view:
#Html.Hidden("urlPrefix", Url.Action("", ""))
// In Javascript
var baseUrl = $("input#urlPrefix").val();
$.ajax({ url: baseUrl + '/Position/AddPromoCode', ...
The part Url.Action("", "") creates '/ApplicationName' part dynamically.
I get server error:status of 500 when I try to send 2 parameters to my action in a controller with jquery ajax, dont know why?
this is the jquery code:
$(".genreLinks").click(function () {
var genreId = $(this).attr("name");
var songId = $("#hiddenRank").val();
$.ajax({
beforeSend: function () { ShowAjaxLoader(); },
url: "/Home/AddPointAndCopyTopTenFavToPlaylist/",
type: "POST",
data: { id: songId, genre: genreId },
success: function (data) {
if (data.status === 1) {
HideAjaxLoader(), ShowMsg("Song Added Successfully")
}
else if (data.status === 4) {
HideAjaxLoader(), ShowMsg("you cannot add your own songs");
}
else if (data.status === 3) {
HideAjaxLoader(), ShowMsg("Cannot add the song. The song was most likely modified or Deleted, we advise you to refresh the page");
}
else if (data.status === 2) {
HideAjaxLoader(), ShowMsg("You cannot add the same song twice");
}
},
error: function () { HideAjaxLoader(), ShowMsg("Song could not be added, please try again") }
});
});
controller's action code that accepts two parameters from my request:
[HttpPost]
public ActionResult AddPointAndCopyTopTenFavToPlaylist(int id, int genre)
{
var song = repository.GetTopTenFav(id);
if (song != null)
{
if (!(song.UserName.ToLower() == User.Identity.Name.ToLower()))
{
foreach (var item in song.Points)
{
if (User.Identity.Name.ToLower() == item.UsernameGavePoint.ToLower())
{
return Json(new { status = 2 }, JsonRequestBehavior.AllowGet);
}
}
var newSong = new Song();
newSong.UserName = User.Identity.Name;
newSong.Title = song.Title;
newSong.YoutubeLink = song.YoutubeLink;
newSong.GenreId = genre;
newSong.Date = DateTime.Now;
repository.AddSong(newSong);
var point = new Point();
point.UsernameGotPoint = song.UserName;
point.UsernameGavePoint = User.Identity.Name;
point.Date = DateTime.Now;
point.Score = 1;
point.TopTenFavId = id;
repository.AddPoint(point);
repository.Save();
return Json(new { status = 1 }, JsonRequestBehavior.AllowGet);
}
else
{
return Json(new { status = 4 }, JsonRequestBehavior.AllowGet);
}
}
else
{
return Json(new { status = 3 }, JsonRequestBehavior.AllowGet);
}
}
I have also registered my new route:
routes.MapRoute(
"AddPoint", // Route name
"{controller}/{action}/{songId},{genreId}", // URL with parameters
new { controller = "Home", action = "", songId = UrlParameter.Optional, genreId = UrlParameter.Optional } // Parameter defaults
);