I am using remote validation to check the availability of username during registration for my asp.net mvc 3 applicaion (C#).
I am using MVC remote Attribute validation as:
[Remote("IsUserNameAvailable", "User")]
public string UserName { get; set; }
I need to show the message on two conditions:
Show error message "Username not available" - Failure Condition
Show Success message "Username available" - Success Condition
I am able to show the Failure Condition's message without any issue like:
return Json("Username not available", JsonRequestBehavior.AllowGet);
But for Success Condition, I need to send true in response(not with the custom message) as:
return Json(true, JsonRequestBehavior.AllowGet);
How can i show custom message for Success Condition of Remote validation?
see this link...
here
One way to achieve that is to add a custom HTTP response header from the validation action:
public ActionResult IsUserNameAvailable(string username)
{
if (IsValid(username))
{
// add the id that you want to communicate to the client
// in case of validation success as a custom HTTP header
Response.AddHeader("X-ID", "123");
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json("The username is invalid", JsonRequestBehavior.AllowGet);
}
Now on the client we obviously have a standard form and an input field for the username:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.UserName)
#Html.ValidationMessageFor(x => x.UserName)
<button type="submit">OK</button>
}
and now the last piece of the puzzle is to attach a complete handler to the remote rule on the username field:
$(function () {
$('#UserName').rules().remote.complete = function (xhr) {
if (xhr.status == 200 && xhr.responseText === 'true') {
// validation succeeded => we fetch the id that
// was sent from the server
var id = xhr.getResponseHeader('X-ID');
// and of course we do something useful with this id
alert(id);
}
};
});
Are you able to return an object (which will be serialised to Json)?
Such as:
var answer = new { success = true, message = "Username available" };
return Json(answer, JsonRequestBehavior.AllowGet);
Then you can parse this in the view.
Also, if you do it this way, but the username is NOT available, you could add a few suggested usernames too.
e.g.
// pretend they chose "dave"
List<string> alternativeNames = new List<string>() { "dave1", "dave2" };
var answer = new { success = false, message = "Username not available", alternatives = alternativeNames };
return Json(answer, JsonRequestBehavior.AllowGet);
Related
I am using an Ajax post method to pass a JSON string to a server-side MVC action. The IActionResult method parses the JSON string into an array which is uploaded into SQL Server via Microsoft.Data.SqlClient methods. The IActionResult returns an Ok() result to the caller upon completion.
The anomaly I have observed is that the database upload (server-side) only completes if I pause/interrupt the browser by placing an alert just after the Ajax method (client-side). My code is as follows:
Client-side:
function ExportJSON() {
var myJson = "some JSON stuff goes here";
$.ajax({
type: "POST",
url: "/Dailies/UploadJson/",
dataType: 'json',
data: { jsonString: myJson },
success: function (data) {
console.log(data);
}
});
alert("Your data has been saved.");
}
Server-side action:
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> UploadJson(string jsonString)
{
if (jsonString != null) {
var myJArray = JsonConvert.DeserializeObject<JArray>(jsonString);
TimePunches[] timePunches = myJArray.ToObject<TimePunches[]>();
try
{
string constr = _configuration.GetConnectionString("MYSQLSERVER");
using (SqlConnection sqlConnection = new SqlConnection(constr)) {
await sqlConnection.OpenAsync();
foreach (TimePunches timePunch in timePunches) {
string query = "INSERT INTO TimePunches([Projectid], [CrewLeaderId]) ";
query += "VALUES(#Projectid, #CrewLeaderId) ";
using (SqlCommand cmd = new SqlCommand(query)) {
cmd.Connection = sqlConnection;
cmd.Parameters.AddWithValue("#Projectid", timePunch.Projectid);
cmd.Parameters.AddWithValue("#CrewLeaderId", timePunch.CrewLeaderId);
await cmd.ExecuteNonQueryAsync();
}
}
sqlConnection.Close();
}
}
catch (Exception ex) {
TempData["msg"] = ex.Message;
}
}
return Ok();
}
To reiterate, the server-side action uploads data to the database (as expected) so long as the alert is present in the client-side ExportJSON() method. Conversely, removing the alert causes the database upload to fail.
Any assistance would be greatly appreciated.
I found an answer to this issue. The following link provides an excellent article explaining the nuances of asynchronous JavaScript calls and, in particular, how to manage synchronization issues when using Ajax: https://stackify.com/return-ajax-response-asynchronous-javascript-call/
In my particular case the solution was as simple as adding an async: false qualifier to my Ajax "post" method.
I am trying to figure out how to prevent a cors error from showing up in developer tools. The way I get the cors error is when I am using an application but in another tab/window I log out of that application but then go back to the other tab and try to do work. Below is my ajax call.
function RemoveScholarshipRequest(id, name) {
if (confirm("Are you sure you want to delete the scholarship request for " + name + "?")) {
var dataSource = $('#Pending').data('kendoGrid').dataSource;
$.ajax({
type: "POST",
url: '#Url.Action("RemoveRequest", "Admin")',
data: {id: id}
}).done(function (response, data, xhr) {
if (response.success) {
dataSource.read();
alert(response.responseText);
}
else if (!response.success) {
if (response.responseText === "Not Authenticated")
alert(response.responseText);
console.log("error", data.status);
//This shows status message eg. Forbidden
console.log("STATUS: "+JSON.stringify(xhr.status));
}
}).fail(function (response) {
console.log(response);
console.log(JSON.stringify(response));
//window.location.href = "/forms/ScholarshipDisbursement/Admin/PendingRequests";
});
}
}
The controller action that the above ajax method calls is below:
[AllowAnonymous]
[HttpPost]
public ActionResult RemoveRequest(string id)
{
if (!User.Identity.IsAuthenticated)
{
return Json(new { success = false, responseText = "Not Authenticated" }, JsonRequestBehavior.AllowGet);
}
if (User.IsInRole("Developer") || User.IsInRole("BannerAdmin"))
{
new ScholarshipRequestStore().DeleteScholarshipRequest(id);
return Json(new { success = true, responseText = "Successfully deleted" }, JsonRequestBehavior.AllowGet);
}
else
{
return Json(new { success = false, responseText = "You are not an authorized user" }, JsonRequestBehavior.AllowGet);
}
}
One way I get around the cors error is by putting AllowAnonymous on the method and then checking for authentication in the method itself but I don't really like that idea. Is there another way of resolving this issue?
Allow anonymous will not solve this, instead you need to send the allow origin header in your api. You can do this by enabling CORs in the startup class as follows
public void ConfigureServices(IServiceCollection services)
{
// Add Cors
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
// Add framework services.
services.AddMvc();
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new CorsAuthorizationFilterFactory("MyPolicy"));
});
...
...
...
}
// This method gets called by the runtime. Use this method to configure
//the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// Enable Cors
app.UseCors("MyPolicy");
//app.UseMvcWithDefaultRoute();
app.UseMvc();
...
...
...
}
and then using the "Enable cors" attribute on your controller
[EnableCors("MyPolicy")]
[AllowAnonymous]
[HttpPost]
public ActionResult RemoveRequest(string id)
read this for better idea https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.2
Note: I have allowed any origin to talk to the API, you can specify whatever origin you want like "https://example.com"
AllowAnonymous won't resolve a "cross-origin" request. The issue you are getting is due to tabbed browsing within your browser having a shared store of authenticated sessions. When you log out in tab 1, the session cookie is removed and then tab 2 is no longer authenticated. This is why AllowAnonymous "works" because without a current authenticated session, you're an anonymous user.
CORS, on the other hand, is when you allow calls to http://myservice.com to come from a different host like http://myclient.com. Anonymous access won't have any impact on that.
I'm trying to implement an extremely simple spike using Nancy as an alternative to ASP.NET MVC.
It should take a username (no password) and provide meaningful error messages on the same login page without requiring a refresh. If login was successful, the response includes the URL to navigate to.
The POCO for the response looks like this:
public class LoginResponseModel
{
public bool IsSuccess { get; set; }
public string RedirectUrl { get; set; }
public string ErrorMessage { get; set; }
}
The JS handler for the login request:
$.ajax({
url: '/login',
type: "POST",
data: { UserName: username }
}).done(function (response) {
if (response.IsSuccess) {
showSuccess();
document.location.href = response.RedirectUrl;
return;
}
showError(response.ErrorMessage);
}).fail(function (msg) {
showError("Unable to process login request: " + msg.statusText);
});
The problem I'm having is with Nancy's Forms-based authentication. I've walked through half a dozen different tutorials which all more or less do the same thing, as well as gone over the Nancy authentication demos. The one thing they all have in common is that they rely on the LoginAndRedirect extension method. I don't want to return a redirect. I want to return a result of the login attempt and let the client handle the navigation.
The IUserMapper implementation I'm using:
public class UserMapper : IUserMapper
{
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
// Don't care who at this point, just want ANY user...
return AuthenticatedUser {UserName = "admin"};
}
}
The relevant part of my LoginModule action:
var result = _userMapper.ValidateUser(input.AccessCode);
if (result.Guid != null) this.Login(UserMapper.GUID_ADMIN, expiry);
return Response.AsJson(result.Response);
but for subsequent requests Context.CurrentUser is always null.
If I add the following method to the Nancy.Demo.Authentication.Forms sample it reproduces the behaviour I'm seeing in my own project, leading me to believe LoginWithoutRedirect doesn't work how I expected.
Get["/login/{name}"] = x =>
{
Guid? userGuid = UserDatabase.ValidateUser(x.Name, "password");
this.LoginWithoutRedirect(userGuid.Value, DateTime.Now.AddYears(2));
return "Logged in as " + x.Name + " now <a href='~/secure'>see if it worked</a>";
};
The problem turns out to be that Context.CurrentUser with FormsAuthentication is dependent upon a cookie which isn't set if you don't return the NancyModule.Login() response.
var result = _userMapper.ValidateUser(input.AccessCode);
if (result.IsSuccess) {
this.LoginWithoutRedirect(result.Guid);
}
return Response.AsJson(result);
In this example, the LoginWithoutRedirect call returns a Response object with the cookie set. To handle this in an Ajax scenario I've had to add a AuthToken property to the LoginAjaxResponse class, then pass the cookie like so:
var result = _userMapper.ValidateUser(input.AccessCode);
var response = Response.AsJson(result);
if (result.IsSuccess) {
var authResult = this.LoginWithoutRedirect(result.Guid);
result.AuthToken = authResult.Cookies[0].Value;
}
return Response.AsJson(result);
On the client, the Ajax response handler changes to (assuming use of jQuery cookie plugin:
$.ajax({
url: '/login',
type: "POST",
data: { UserName: username }
}).done(function (response) {
if (response.IsSuccess) {
showSuccess();
$.cookie("_ncfa", response.AuthToken); // <-- the magic happens here
document.location.href = response.RedirectUrl;
return;
}
showError(response.ErrorMessage);
}).fail(function (msg) {
showError("Unable to process login request: " + msg.statusText);
});
The AuthToken is the GUID which has been encrypted and base64-encoded. Subsequent requests with this.RequiresAuthentication() enabled will first check for this auth token cookie.
If no "_ncfa" cookie is present,the UserMapper's GetUserFromIdentifier() is never called.
If the value in Context.Request.Cookies["_ncfa"] does not result in a valid GUID when base64-decoded and decrypted, GetUserFromIdentifier() is never called.
If GetUserFromIdentifier() isn't called, Context.CurrentUser is never set.
If you want the source for a working example it's on GitHub.
LoginAndRedirect is only one option, there are equivalent methods for not redirecting (LoginWithoutRedirect), or one that picks up on whether it's an AJAX request and handles it appropriately (Login). The same applies to logging out.
This is all covered, in detail, in the documentation.
I understand the reason to have the business logic in both client and server, but I don't understand well how to do that in some situations. Here for example:
// client/client.js
// hnadling click event on the Create Accounts button
Template.homecontent.events({
'click #btnCreateAccount': function (event, template) {
var userEmail = template.find('#email').value,
userName = template.find('#newusername').value,
password = template.find('#newpassword').value,
password2 = template.find('#password2').value,
name = template.find('#fullname').value;
validates = true;
//do some validation here
if(password != password2) {
validates = false;
}
if(validates === true) {
Accounts.createUser({
username: userName,
email: userEmail,
password: password,
profile: {
name: name
}
}, function (error) {
if (error) {
console.log("Cannot create user");
}
});
}
}
});
Since the validation is on the client only, it can easily be bypassed.
But there's a problem here: this is triggered by a user event, so I'm not sure what's the best way to have this code running on client & server.
You may be looking for something like Meteor.methods();, which allows you to define functions on the server that the client can call using Meteor.call(). You could provide a validation function and a user save function on the server, and call them both from the client, passing in the form data.
What I have done in the past is (on the client) I have a userFormParse() function that takes a form object and parses it into an object that can be passed into a server side validation function. I use the same userFormParse function for user edit and creation forms.
The validation function returns an error object to the client, or, if it's all valid data, I'll pass the data object on to a userCreateWithRole function (I usually always have roles assigned to users).
On the server:
Meteor.methods({
'createUserWithRole': function(data, role) {
var userId;
Meteor.call('createUserNoRole', data, function(err, result) {
if (err) {
return err;
}
Roles.addUsersToRoles(result, role);
return userId = result;
});
return userId;
},
'createUserNoRole': function(data) {
//Do server side validation
return Accounts.createUser({
email: data.email,
password: data.password,
profile: data.profile
});
}
});
And then on the client:
Template.userSignup.events({
'submit #userSignup': function(event) {
var data, validationErrors;
event.preventDefault();
data = userInputParse($(event.target)); //this function parses form into user object that can be inserted
validationErrors = userObjectValidate(data); //this function takes and does client side validation on the user object.
data.profile.status = 0;
if (validationErrors) {
//Show the user the validation errors
} else {
return Meteor.call('createUserWithRole', data, 'standard', function(err, userId) {
if (!err) {
//User created!!
} else {
//Insertion Error
}
});
}
}
});
That code is conceptual and untested :)
You should be doing it on server side, using Accounts.onCreateUser
The previous answers are not really exact.
Creating and using a Meteor method won't stop users to call the Accounts.createUser from the console for example. Therefore you also need to prevent the creation of users on the client :
Accounts.config({
forbidClientAccountCreation : true
});
You might want to look into Accounts.validateNewUser.
Example (taken from the docs):
Accounts.validateNewUser(function (user) {
if (user.username && user.username.length >= 3)
return true;
throw new Meteor.Error(403, "Username must have at least 3 characters");
});
I am trying to create controller actions which will return either JSON or partial html depending upon a parameter. What is the best way to get the result returned to an MVC page asynchronously?
In your action method, return Json(object) to return JSON to your page.
public ActionResult SomeActionMethod() {
return Json(new {foo="bar", baz="Blech"});
}
Then just call the action method using Ajax. You could use one of the helper methods from the ViewPage such as
<%= Ajax.ActionLink("SomeActionMethod", new AjaxOptions {OnSuccess="somemethod"}) %>
SomeMethod would be a javascript method that then evaluates the Json object returned.
If you want to return a plain string, you can just use the ContentResult:
public ActionResult SomeActionMethod() {
return Content("hello world!");
}
ContentResult by default returns a text/plain as its contentType.
This is overloadable so you can also do:
return Content("<xml>This is poorly formatted xml.</xml>", "text/xml");
I think you should consider the AcceptTypes of the request. I am using it in my current project to return the correct content type as follows.
Your action on the controller can test it as on the request object
if (Request.AcceptTypes.Contains("text/html")) {
return View();
}
else if (Request.AcceptTypes.Contains("application/json"))
{
return Json( new { id=1, value="new" } );
}
else if (Request.AcceptTypes.Contains("application/xml") ||
Request.AcceptTypes.Contains("text/xml"))
{
//
}
You can then implement the aspx of the view to cater for the partial xhtml response case.
Then in jQuery you can fetch it passing the type parameter as json:
$.get(url, null, function(data, textStatus) {
console.log('got %o with status %s', data, textStatus);
}, "json"); // or xml, html, script, json, jsonp or text
Another nice way to deal with JSON data is using the JQuery getJSON function. You can call the
public ActionResult SomeActionMethod(int id)
{
return Json(new {foo="bar", baz="Blech"});
}
Method from the jquery getJSON method by simply...
$.getJSON("../SomeActionMethod", { id: someId },
function(data) {
alert(data.foo);
alert(data.baz);
}
);
I found a couple of issues implementing MVC ajax GET calls with JQuery that caused me headaches so sharing solutions here.
Make sure to include the data type "json" in the ajax call. This will automatically parse the returned JSON object for you (given the server returns valid json).
Include the JsonRequestBehavior.AllowGet; without this MVC was returning a HTTP 500 error (with dataType: json specified on the client).
Add cache: false to the $.ajax call, otherwise you will ultimately get HTTP 304 responses (instead of HTTP 200 responses) and the server will not process your request.
Finally, the json is case sensitive, so the casing of the elements needs to match on the server side and client side.
Sample JQuery:
$.ajax({
type: 'get',
dataType: 'json',
cache: false,
url: '/MyController/MyMethod',
data: { keyid: 1, newval: 10 },
success: function (response, textStatus, jqXHR) {
alert(parseInt(response.oldval) + ' changed to ' + newval);
},
error: function(jqXHR, textStatus, errorThrown) {
alert('Error - ' + errorThrown);
}
});
Sample MVC code:
[HttpGet]
public ActionResult MyMethod(int keyid, int newval)
{
var oldval = 0;
using (var db = new MyContext())
{
var dbRecord = db.MyTable.Where(t => t.keyid == keyid).FirstOrDefault();
if (dbRecord != null)
{
oldval = dbRecord.TheValue;
dbRecord.TheValue = newval;
db.SaveChanges();
}
}
return Json(new { success = true, oldval = oldval},
JsonRequestBehavior.AllowGet);
}
To answer the other half of the question, you can call:
return PartialView("viewname");
when you want to return partial HTML. You'll just have to find some way to decide whether the request wants JSON or HTML, perhaps based on a URL part/parameter.
Alternative solution with incoding framework
Action return json
Controller
[HttpGet]
public ActionResult SomeActionMethod()
{
return IncJson(new SomeVm(){Id = 1,Name ="Inc"});
}
Razor page
#using (var template = Html.Incoding().ScriptTemplate<SomeVm>("tmplId"))
{
using (var each = template.ForEach())
{
<span> Id: #each.For(r=>r.Id) Name: #each.For(r=>r.Name)</span>
}
}
#(Html.When(JqueryBind.InitIncoding)
.Do()
.AjaxGet(Url.Action("SomeActionMethod","SomeContoller"))
.OnSuccess(dsl => dsl.Self().Core()
.Insert
.WithTemplate(Selector.Jquery.Id("tmplId"))
.Html())
.AsHtmlAttributes()
.ToDiv())
Action return html
Controller
[HttpGet]
public ActionResult SomeActionMethod()
{
return IncView();
}
Razor page
#(Html.When(JqueryBind.InitIncoding)
.Do()
.AjaxGet(Url.Action("SomeActionMethod","SomeContoller"))
.OnSuccess(dsl => dsl.Self().Core().Insert.Html())
.AsHtmlAttributes()
.ToDiv())
You may want to take a look at this very helpful article which covers this very nicely!
Just thought it might help people searching for a good solution to this problem.
http://weblogs.asp.net/rashid/archive/2009/04/15/adaptive-rendering-in-asp-net-mvc.aspx
PartialViewResult and JSONReuslt inherit from the base class ActionResult. so if return type is decided dynamically declare method output as ActionResult.
public ActionResult DynamicReturnType(string parameter)
{
if (parameter == "JSON")
return Json("<JSON>", JsonRequestBehavior.AllowGet);
else if (parameter == "PartialView")
return PartialView("<ViewName>");
else
return null;
}
For folks who have upgraded to MVC 3 here is a neat way
Using MVC3 and Json
public ActionResult GetExcelColumn()
{
List<string> lstAppendColumn = new List<string>();
lstAppendColumn.Add("First");
lstAppendColumn.Add("Second");
lstAppendColumn.Add("Third");
return Json(new { lstAppendColumn = lstAppendColumn, Status = "Success" }, JsonRequestBehavior.AllowGet);
}
}
Flexible approach to produce different outputs based on the request
public class AuctionsController : Controller
{
public ActionResult Auction(long id)
{
var db = new DataContext();
var auction = db.Auctions.Find(id);
// Respond to AJAX requests
if (Request.IsAjaxRequest())
return PartialView("Auction", auction);
// Respond to JSON requests
if (Request.IsJsonRequest())
return Json(auction);
// Default to a "normal" view with layout
return View("Auction", auction);
}
}
The Request.IsAjaxRequest() method is quite simple: it merely checks the HTTP headers for the incoming request to see if the value of the X-Requested-With header is XMLHttpRequest, which is automatically appended by most browsers and AJAX frameworks.
Custom extension method to check whether the request is for json or not so that we can call it from anywhere, just like the Request.IsAjaxRequest() extension method:
using System;
using System.Web;
public static class JsonRequestExtensions
{
public static bool IsJsonRequest(this HttpRequestBase request)
{
return string.Equals(request["format"], "json");
}
}
Source : https://www.safaribooksonline.com/library/view/programming-aspnet-mvc/9781449321932/ch06.html#_javascript_rendering