How to check whether username already exists using ajax in asp.net? - asp.net-mvc

I am working on an application which has a registration form and I have to display to the user whether the username exists or not.
I am using asp.net mvc3 and planned to use AJAX to achieve this.
I have a form
<tr>
<td>User Name*</td>
<td><input id="UserName" name="UserName" type="text" onblur="check(this.value);"/></td>
<td id= "UName"></td>
</tr>
which calls a .js file that has the folling contents
function check(User) {
...
var url = "/UserNameCheck/Index";
url += "?User=" + User;
xmlHttp.onreadystatechange = state_Change;
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
}
function state_Change() {
if (xmlhttp.readyState == 4) {// 4 = "Response loaded"
if (xmlhttp.status == 200) {// 200 = Response Error Free
document.getElementById("UName").innerHTML = xmlHttp.responseText
}
else {
alert("Problem retrieving XML data");
}
}
}
I alerted the username and I am getting the correct value that i had entered. Now, the URL is /UserNameCheck/Index where UserNameCheck is a controller and Index is a method in that.
The controller has this code.
public ActionResult Index(string User)
{
string UserName;
try
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
UserName = Request.QueryString["User"];
ConnectionPackage.ConnectionClass cc = new ConnectionPackage.ConnectionClass();
conn = cc.con;
string sql = "Select UserName FROM UserDetails where UserName = '" + UserName + "'";
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.CommandType = CommandType.Text;
object p = cmd.ExecuteScalar();
cmd.ExecuteNonQuery();
string u = (string)p;
if (u.Length==0 || u.Equals("NULL") || u.Equals("null")||u.Equals("Null"))
{
return View();
}
return null;
}
catch (Exception ex){
}
and the view has
String buffer = " <table><tr><td id = 'UName' >" This user name already exists. Please select some other unser name.
buffer = buffer + "</td></tr></table>";
response.getWriter().println(buffer);
I also tried writing
Response.Clear();
Response.Write("UserName already exists. Please select another UserName");
Response.End();
instead of returning View.
But in both the cases, I didn't get any message that the username exists even though I typed a user name that was already present in the database.
The connection string work for inserting into the database, so I dont think there is a problem with that. Is there a problem with the URL that I have mentioned in the js file? Or is my entire approach wrong?
I am basically from java background so dont know much about asp.net. Please do help.
Thank you very much in advance.

I followed what was given in MSDN article How to: Implement Remote Validation in ASP.NET MVC
jQuery in Action is the most popular jQuery book

You're doing alright but you could make this a whole lot easier on yourself. If you are usinng MVC3 with Razor, your app already has jQuery installed.
Use the $.ajax() method to perform the calls to your controller action that checks names...
Bind the $.ajax() call "unobtrusively" which means instead of on your HTML control, bind the event to your control from the jquery/javascript.
Second, if you want a little fancy performance, you can bind it via the live() jquery function or keyup event, so that as you are typing the ajax call is made and you find out realtime.
Ultimately you will end up with a lot less javascript, and your JS stuff will be cleanly separated from your markup.
As far as your controller action is going, it looks fine for playing around and learning, but you'd want to think about either (a) putting your SQL statement as a stored procedure on the db server and calling that, or (b) writing a repository pattern class and then using LINQ to do your query work after the DB fetch.
Another possibility would be to use Entity Framework 4.1 via NuGet to eliminate both needs. It can have a bit of a learning curve, but there's lots of good stuff out there and your example would be fairly simple to get started with.
Let me know if you have any specific concerns with your code and I can provide a more detailed answer.

Related

Read API Response Messages

I have Developed ASP.net Core Web API & Web APP Which are in the same solution but different projects.
in the API I have some validations/checking as you may call.
e.g: if user email already exists, the API returns 'Email alreday in use' like this
bool EmailExists = dbContext.Users.Any(u => u.Email == user.Email);
if (EmailExists)
{
return new JsonResult("Email Address already taken!, Try a differen Email");
}
and so on. in some cases I may need to check multiple columns one a time, (eg: UserName, Email, TellNum)
This is an example of calling the API in the MVC
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseAPIUrl);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage Res = await client.PostAsJsonAsync("Users", user);
if (Res.IsSuccessStatusCode)
{
//in here I want check the `Res` and if it contains the returned messages, I want to display them by assigning it to `TempData[infoMsg]`
// else some something(register user)
}
// Check the returned JsonResult messages here if statusCode is ultered eg: BadRequest
}
My Question is how can I display these types of response messages in razor view in the MVC(Web App). in PostMan its workin, returning the response messages in body.
I did a lot of research about this but couldn't come to conclusion. I also cantacted some Devs I know(not .NET) and they said use JavaScript to call your API, which means I have to change almost everything I have done so far.
I aslo tried ultereing the statuCode to something like BadRequest in the API(if Email exists) in which case it will be checked outside the if (Res.IsSuccessStatusCode) of the Httpclient.
any help or direction is highly appreciated.
You should return a http error and a body containing some data about it eg field and message to your mvc controller. That could be a 422 error or whatever you like really since it's effectively internal and just coming back to the mvc controller.
The controller can then add any such error to modelstate and you can use the razor model "client" validation mechanism to show the error associated with a field.
This is therefore using the same mechanism used for attribute validation in the controller where you'd do
if (!ModelState.IsValid)
This is air code but will hopefully give you the idea.
[HttpPost]
public ActionResult PostUpdate(User u)
{
// call service and await response
var response = await httpClient.PostAsJsonAsync(posturi, u);
var returnContent = await response.Content.ReadAsAsync<ReturnContent>();
if (response.Result != HttpStatusCode.OK)
{
ModelState.AddModelError(returnContent.FieldName,returnContent.Error);
return Page();
}
// etc
You will want a more sophisticated checking on errors of course and check you get the body you're expecting.
Maybe you just hard code the field and error message if there's only one possibility. Maybe work with an array of fields and errors if there could be numerous validation fails.

how to access ViewBag together with Json

I am implementing autocomplete functionality by returning database values with the help of Json and Linq.I need to implement edit functionality. For edit functionality I need to return viewBag data to my view. But I couldn't access my viewBag while returning Json result.
public JsonResult Index(string Prefix,int id = 0)
{
SqlConnection sqcon2 = new SqlConnection(conn);
SqlCommand cmd2 = new SqlCommand();
SqlDataAdapter sd2 = new SqlDataAdapter(cmd2);
DataTable dt2 = new DataTable();
cmd2.Connection = sqcon2;
cmd2.CommandText = "sps_userLocationByID";
cmd2.CommandType = System.Data.CommandType.StoredProcedure;
cmd2.Parameters.AddWithValue("#id", id);
sqcon2.Open();
sd2.Fill(dt2);
sqcon2.Close();
foreach (DataRow dr2 in dt2.Rows)
{
ViewBag.cityName = dr2["CityName"].ToString();
ViewBag.Name = dr2["Name"].ToString();
}
SqlConnection sqcon = new SqlConnection(conn);
SqlCommand cmd = new SqlCommand();
SqlDataAdapter sd = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
cmd.Connection = sqcon;
cmd.CommandText = "sps_userCity";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
sqcon.Open();
sd.Fill(dt);
sqcon.Close();
List<CityModel> ObjList = new List<CityModel>();
foreach (DataRow dr in dt.Rows)
{
CityModel st = new CityModel();
st.CityName = dr["CityName"].ToString();
ObjList.Add(st);
}
var CityName = (from N in ObjList
where
N.CityName.ToLower().StartsWith(Prefix.ToLower())
select new { N.CityName });
return Json(CityName, JsonRequestBehavior.AllowGet);
}
you can use viewbag if you call another Actionresult method via Ajax.
ViewBag only exists in the context of a Razor view, which itself only exists if you're returning View(), PartialView(), etc. If you're returning JSON, that's all you get. You cannot utilize ViewBag.
That said, I think you're confused in general about how things work though. Initially your app is responding with an HTML document. Everything else, your action, view, etc. is all about getting to this HTML document that can finally be returned to the client, a web browser in this case.
Once that response has been sent. The server is done. Client-side, the web browser parses the document and creates various object models: the DOM, CSSOM, etc. It then uses these object models to render the page (the paint). During and after this process, your JavaScript is run. All of this is happening client-side, and the server doesn't know or care; it's already done its job.
Then, you're wanting some new information from the server. That requires a new request from the client, which will result in a new response from the server. That request could come in the form of navigation by the user within the browser (in which case the whole browser window/tab view will change) or in the form of an AJAX request, which leaves the current view in the browser window/tab as-is.
XMLHttpRequest, the actual JavaScript client class responsible for making AJAX requests, is what's referred to as a "thin client". It makes requests and receives responses, but it does not do anything with said response, in ontrast to a "thick client" like a web browser, which takes the response and does all the object model creation and rendering. Instead, a callback function is merely invoked. It is your responsibility to do something in that callback with the response you receive (update the DOM, etc.)
All this is to say that doing anything with ViewBag while returning a JSON response to an AJAX request makes absolutely zero sense, because the view is not changing. You seem to be imagining that change ViewBag would somehow automatically update something on the page that was previously added via ViewBag in your initial Razor view. That's not the case, and it's not how things work. If you want to alter something on the page, the new value needs to be returning as part of the JSON response from your AJAX request, and then you must use that new value to alter the DOM accordingly.

Post Data to next Page in Custom Save Action Web Form For Marketer Sitecore 8

After saving data in WFFM Custom save Action,I want to redirect Success Page with some large amount of data
I am trying below line of code .
I Can use Cookies ,session or Query String and Response.Redirect(baseUrl)but i want to Cookies ,session or Query String .
class SaveAction : WffmSaveAction
{
public override void Execute(ID formId, AdaptedResultList adaptedFields, ActionCallContext actionCallContext, params object[] data)
{
//Save Data in Service ,, Redirect to success page with below code with some data like ID
string baseUrl = HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority +
HttpContext.Current.Request.ApplicationPath.TrimEnd('/') + "/success-page";
HttpContext.Current.Response.Clear(); //
StringBuilder sb = new StringBuilder();
sb.Append("<html>");
sb.AppendFormat(#"<body onload='document.forms[""form""].submit()'>");
sb.AppendFormat("<form name='form' action='{0}' method='post'>", baseUrl);
sb.AppendFormat("<input type='hidden' name='id' value='{0}'>", "123456");
// Other params go here
sb.Append("</form>");
sb.Append("</body>");
sb.Append("</html>");
HttpContext.Current.Response.Write(sb.ToString());
HttpContext.Current.Response.End();
// HttpContext.Current.Response.Redirect(baseUrl);
}
}
Above code reload the same page with no body in Html.
Am i missing something in given code ?
The answer might depend on whether you have an mvc form or not.
In case of mvc forms, you might want to read this: http://ggullentops.blogspot.be/2016/07/sitecore-wffm-act-on-success.html. It describes hooking into the success pipeline <wffm.success> and passing data towards the success page (how exactly - querystring, session, .. is up to you(r code)). Fairly easy once you know the correct pipeline.
There is also a great post here describing what you are trying to do - i.e. saving the data for later use in the saveaction. It's too much code to copy here but it comes down to saving the data (in session) during the save action and creating a rendering (to read and handle the data again) that you will place on the success page.
Creating a rendering to place on your success page is something you will have to do anyway.. Don't try to redirect yourself, Sitecore does that for you.

Getting the Id of an error in Elmah after calling .Raise()

I'm working on an MVC3 application and I'm using Elmah to handle my error logging. What I want in my application is to carry the Elmah Id onto the custom error page as I will provide a link which allows a user to specifically report it in the event that it is a repeat error (in their opinion).
Now, I've read similar questions on here and they suggest adding the following code (or similar) to the Global.asax.cs file:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
string sessionId = Session.SessionID;
Session["ElmahId_" + sessionId] = args.Entry.Id;
}
This is what I'm using at the moment, with the SessionID allowing for added flexibility in making the Session stored object unique. However, this may still cause issues if more than one error occurs at (virtually) the same time.
Instead, I decided to work on my own HandleErrorAttribute that looks something like this:
public class ElmahHandleErrorAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (filterContext.IsChildAction && (!filterContext.ExceptionHandled
&& filterContext.HttpContext.IsCustomErrorEnabled))
{
Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception);
// get error id here
string errorId = null;
string areaName = (String)filterContext.RouteData.Values["area"];
string controllerName = (String)filterContext.RouteData.Values["controller"];
string actionName = (String)filterContext.RouteData.Values["action"];
var model = new ErrorDetail
{
Area = areaName,
Controller = controllerName,
Action = actionName,
ErrorId = errorId,
Exception = filterContext.Exception
};
ViewResult result = new ViewResult
{
ViewName = "Error",,
ViewData = new ViewDataDictionary<ErrorDetail>(model),
TempData = filterContext.Controller.TempData
};
filterContext.Result = result;
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
}
where ErrorDetail is a custom model which just has the public properties that are being set here as strings. This data can then be shown in the model for admin's at a quick glance and the errorId can be used to create the 'Report Error' link.
So my question is does anyone know of a way of getting the Id after the line
Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception)
without using the Logged event in the global.asax.cs?
Any thoughts are much appreciated.
After reading Dupin's comments it seems logical that it isn't quite possible. I tried digging around the Elmah source code and came up with a couple of alternatives that might be worth sharing.
The obvious alternative is stick with my original option of using the Logged event:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
string sessionId = Session.SessionID;
Session["ElmahId_" + sessionId] = args.Entry.Id;
}
For a more direct solution it is possible to manually log the error with the following:
string errorId = Elmah.ErrorLog.GetDefault(HttpContext.Current)
.Log(new Elmah.Error(filterContext.Exception));
However, using this approach won't hit your filters or mail module and so on.
After doing a bit of thinking and a little more searching, I came up with a new compromise. Still using the logged event but I've found a way to create a new unique key that can be passed to the view, by adding my own data to the exception.
string loggingKey = "ElmahId_" + Guid.NewGuid().ToString();
filterContext.Exception.Data.Add("LoggingKey", loggingKey);
This way I can pass the exception in my view model, which has this key value in the Data collection. The logged event would be changed to something like this:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
string key = args.Entry.Error.Exception.Data["LoggingKey"].ToString();
Session[key] = args.Entry.Id;
}
Then in the view I get the key from the model to then pull the Id from the Session collection.
Maybe not very helpful but I suspect you can't get the error id at that point and you will need to use the logged event.
When you call
Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception)
You're just raising the error. Depending on how you've configured ELMAH you might be logging the error or you might just send an email or a tweet.
There's no direct link between a raised error and an Id. That will only come with logging which, if you're feeling funny, you could be doing in multiple places and so creating multiple ids.
http://code.google.com/p/elmah/issues/detail?id=148#c3 is an identical request and a proposed patch on the Elmah project site
The solution above only works only if there is a Session object (website scenario). We needed it to work in an Azure WorkerRole, or a console / desktop app type setup. This solution will also work for web and save some session memory. There isn't a perfect solution, but one that worked for us to be able to log the error and retrieve the stored ID AND fire off an email is to:
Store the error using ErrorLog.Log(error) (see: Using ELMAH in a console application)
Raise the error skipping the logging (SQL or otherwise)
For the second part, we used the implementation of ElmahExtension given here: https://stackoverflow.com/a/2473580/476400
and REMOVED the following lines adding the logging:
(ErrorLog as IHttpModule).Init(httpApplication);
errorFilter.HookFiltering(ErrorLog); //removed!
The entire call from our client code looks like this:
ErrorLog errorLog = ErrorLog.GetDefault(null);
errorLog.ApplicationName = "YourAppName";
Error error = new Error(ex);
string errorResult = errorLog.Log(error);
Guid errorId = new Guid(errorResult);
ex.LogToElmah(); //this is just going to send the email
You might want to call that extention method something else, like RaiseToElmahNoStorage(), or something to indicate it is skipping the storage component.

How to display validation errors on a page with multiple forms in ASP.NET MVC

I have a top-level page called ReceiveItem. Within that page, I have a couple different FORMs - only one of which will be filled out and submitted (depending upon how that item is received). This all works quite well from the UI perspective. Here is the general page structure:
<ReceiveItem.aspx>
<ReceiveNewInventory.ascx>
<ReceiveOrderReturn.ascx>
<ReceiveFromLoan.ascx>
Except, I do not know how to properly display validation errors. Here is my controller for one of those forms:
public ActionResult ReceiveNewInventory(
int id,
int vendorId,
int quantity,
decimal cost) {
var db = new Data();
var item = db.ItemSet.First(i => i.Id == id);
var vendor = db.BusinessSet.First(i => i.Id == vendorId);
ValidateCost(cost);
ValidateQuantity(quantity);
if (ModelState.IsValid) {
item.AddNewInventory(vendor, quantity, cost);
TempData["Message"] = "Added " + quantity +
" inventory items to " + item.FullDisplayName;
return RedirectToAction("Index");
}
else {
TempData["Quantity"] = quantity;
TempData["VendorId"] = vendorId;
TempData["Cost"] = cost;
return RedirectToAction("ReceiveItem", new { id });
}
}
I would like to display the model errors that the two validation functions add using the simple Html.ValidationSummary function; but, those errors seem to get lost because I do the RedirectToAction. (My ReceiveNewInventory controller action does not have a view directly associated with it.)
With the one condition that I still want 1 page with multiple FORMs, what can I change about this design so that my validation messages show up on the ReceiveItem page?
You need to put the ModelState into TempData and extract it in your ReceiveItem action method. Alternatively, change the Redirect to a return View()
hth
Dan
Look at NerdDinner and see how they do it. Very neat and you can display a summary at the top of the page as well as text next to each item if you wish.
let me know if you have trouble and I'll post code.
Why do you redirect to ReceiveItem even if you have errors? When you display the validation message, don't you want the user to have the opportunity to fix their mistakes? If so, why not keep them on the RecevieNewInventory page again and return the view?

Resources