I am trying to pass some text from a textbox to a controller to get JSON results like so
function invokeAction() {
var searchText = $("#SearchTextBox").val();
// Invoke MVC controller action
$.getJSON("/Home/Results/" + searchText, bindResults);
}
If I put an alert here I can see that searchText definately has a value, but when I put a break point on this controller action:
public ActionResult Results(string search)
{
var r = from t in db.Restaurants
where SqlMethods.Like(t.Name, "%" + search + "%") || SqlMethods.Like(t.Postcode, search + "%") || SqlMethods.Like(t.CuisineType.Type, search + "%")
orderby t.Name ascending
orderby t.Rating descending
orderby t.NumOfViews
descending
select t;
return Json(r.ToList());
}
The string passed in is null, yet when I check the http context in the debugger my searchtext is a part of the url.
As this is null the query returns no results.
Am I missing something here?
I've had some problems returning json from services and I wasn't getting any calls back. it turned out that the json was malformed and I was able to test that and get those errors by handling the error option of the plain ajax call.
$.ajax({
type: "GET",
url: "Home/Results/",
data: { search: searchText },
dataType: "json",
error: function(xhr, status, error) {
// you may need to handle me if the json is invalid
// this is the ajax object
},
success: function(json){
alert( "Data Returned: " + json);
}
});
You will have to fix your route and replace {id} with {search} in order to get it to bind to the correct parameter - try something like this:
routes.MapRoute("search", "Home/Results/{search}",
new { controller = "Home", action = "Results" });
If you don't want to do that, you can do it like this by specifying the parametername as a standard querystring paramter
$.getJSON("/Home/Results?search=" + searchText,bindresults);
that will fix the binding.
Related
I am new to .Net Core and MVC. I've got several Razor Pages that allow users to post stuff like comments and ratings for individual posts, but I want to try something new for liking a post so that it can be done using javascript without refreshing the page. To do this I am trying to pass data from the page to a service class via an API controller using jQuery ajax. Unfortunately every time I try to pass a simple value to the controller I get a an error 400 saying that the value cannot be converted to System.WhateverObjectTypeITry. For instance if I try to pass it as an integer I get "The JSON value could not be converted to System.Int32" and if I try to pass it as a string I get "The JSON value could not be converted to System.String"
My API controller looks like this:
[HttpPost]
[Route("AddPostLike")]
public async Task<string> AddPostLike([FromBody] int postid)
{
if(_signInManager.IsSignedIn(User))
{
ApplicationUser user = await _userManager.GetUserAsync(User);
Likes newLike = new Likes();
newLike.Post = Convert.ToInt32(postid);
newLike.Commentid = null;
newLike.Userid = user.Id;
await _likeService.LikeAsync(newLike);
return $"liked";
}
else
{
return $"User Must Be Logged in to Like";
}
}
My jQuery in the Razor Page looks like this:
<script>
$(document).ready(function () {
$('#like-post').on('click', function () {
var postid = parseInt($('#idlabel').text());
$.ajax({
url: '/api/Likes/AddPostLike/',
type: 'POST',
dataType: 'text',
data: '{"postid":"' + postid + '"}',
contentType: 'application/json',
success: function () {
var likecount = parseInt($('#like-post-count').text());
likecount + 1;
$('#like-post-count').text(likecount);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("responseText=" + XMLHttpRequest.responseText + "\n textStatus=" + textStatus + "\n errorThrown=" + errorThrown);
}
});
});
});
</script>
I am using .Net Core 5.0 and am trying to use the Contoso Crafts demo as a guide, but the people at Microsoft that built Contoso Crafts decided to use a Blazor component instead of a razor page which somehow communicates with controllers despite not requiring the developer to write any javascript (see https://github.com/dotnet-presentations/ContosoCrafts/blob/master/src/Components/ProductList.razor) and they don't use a database to store data (they use a static JSON file), so I've had to go out and find a solution that might actually work in the real world.
Your [FromBody] attribute is saying that the body should be parseable as an integer, but the body is actually something like {"postid":"13"}.
The body is an object with a property named postId. So try defining that as your DTO:
public sealed class AddPostLikePostDto
{
public int PostId { get; set; }
}
...
public async Task<string> AddPostLike([FromBody] AddPostLikePostDto dto)
If you just want to post an Integer data, just change like below:
data: JSON.stringify(postid)
but the success function did not work.
That is because the counter does not increase, you can use likecount++ or likecount = likecount + 1 to make it work.
Another problem seems to be with the response from the controller which never tells people they must be logged in if the user is not signed in like it should.
That is because the else clause in your backend is a correct response for the ajax, you can simply throw an exception like:throw new Exception("User Must Be Logged in to Like");
Another way, you can change your code like below:
[HttpPost]
[Route("AddPostLike")]
public async Task<IActionResult> AddPostLike([FromBody] int postid)
{
if (xxx)
{
return Ok("liked");
}
else
{
//return $"User Must Be Logged in to Like";
return BadRequest("User Must Be Logged in to Like");
}
}
A whole working demo:
<label id="idlabel">1</label>
<label id="like-post-count" >4</label>
<input id="like-post" type="button" value="Post"/>
#section Scripts
{
<script>
$(document).ready(function () {
$('#like-post').on('click', function () {
var postid = parseInt($('#idlabel').text());
$.ajax({
url: '/api/Likes/AddPostLike/',
type: 'POST',
dataType: 'text',
data: JSON.stringify(postid), //change here....
contentType: 'application/json',
success: function () {
var likecount = parseInt($('#like-post-count').text());
likecount++;
$('#like-post-count').text(likecount);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("responseText=" + XMLHttpRequest.responseText + "\n textStatus=" + textStatus + "\n errorThrown=" + errorThrown);
}
});
});
});
</script>
}
Controller:
[HttpPost]
[Route("AddPostLike")]
public async Task<string> AddPostLike([FromBody] int postid)
{
if (xxxx)
{
return $"liked";
}
else
{
//return $"User Must Be Logged in to Like";
throw new Exception("User Must Be Logged in to Like");
}
}
In addition to fixing the way data can be sending to controller ( #StephenCleary already offered one of the possible ways), you have to fix a success function too by adding a result parameter
success: function (result) {
var likecount = parseInt(result).text());
likecount + 1;
$('#like-post-count').text(likecount);
},
I am working in ASP.Net MVC. I have following Javascript code in which i am calling a controller-method which returns a view. I want to send parameters to a controller method which re
function fun(p1,p2)
{
// code here to call controller method which returns view
}
public ActionResult ProblemDetails(p1,p2)
{
// here goes some code.
return View();
}
Please tell me the code which can be used to call controller and send parameters too.
Action Method
public ActionResult SendStream(string a, string b)
{
}
JQuery/JSON
Please note that Get Verb will not support complex Data parameters due to it's Query string length constraint. So use POST Verb instead of GET Verb while sending large data
$.ajax({
url: url,
data: JSON.stringify({ a: "a", b: "b" }), //Two String Parameters
type: 'GET', //For Submit, use POST
contentType: 'application/json, charset=utf-8',
dataType: 'json'
}).done(function (data) {
//Success Callback
}).fail(function (data) {
//Failed Callback
}).always(function(data) {
//Request completed Callback
});
Are you perhaps looking to return a Partial View? You can use jQuery ajax to post to a controller method that returns a Partial View (html). You can then render that HTML on the page.
http://mazharkaunain.blogspot.com/2011/04/aspnet-mvc-render-partial-view-using.html
jQuery.get is the shorthand way to achieve this.
function fun(p1,p2)
{
var url = '/controller/ProblemDetails?p1=' + p1 + '&p2=' + p2;
$.get(url, function (data) {
// data will be your response as html
});
}
I might also suggest to have the action return PartialView() instead of View() since you will not return the layout along with the response. It all depends on your intentions for the returned html.
There are several ways to do it.
For example Ajax:
Quick note first: Make sure that in your MVC routing configuration you have a route configured to reflect the following url below:
function fun(p1,p2)
{
var url = '/ControllerName/ProblemDetails?p1=p1&p2=p2' //url to your action method
$.ajax({
url:url,
type:'post' or 'get', //(depending on how you're doing this. If post you can pass data internally instead of query string ),
dataType:'html', //(for example)
success:function(data){
//data here will contain your View info so you can append it do div for example. You can use JQuery .html() function for that
error: function (xhr) {
//catch error
}
}
})
}
Another way, in case if you want to load your View data to DIV is to use JQUery functions such as .load();
function fun(p1,p2)
{
var url = '/ControllerName/ProblemDetails?p1=p1&p2=p2';
$('#YourDivTagForExample').load(url);
}
$.ajax call can also be abbriviated to $.get, $.post or $.getJSON depending on what kind of a call you want to make to your action method. There is a lot more to it too.
Finally make sure to take a look at this answer. Your question was actually already answered in full:
Correct way to handle Ajax calls in ASP.Net MVC 3
Use JSONResult instead of ActionResult and Manipulate return data in javascript.
I currently have a JSON object on my page which gets built up as users add items to it. This is all done in JavaScript/jQuery.
When the user is done adding items, I want to POST this object to a controller action and have the action return a strongly typed view using this data.
Currently, I have the jQuery.ajax POST sending this JSON object to an Action Method which then binds this object to my strongly typed Model. Problem is, I actually want this jQuery.ajax POST to redirect as if the JSON object were in a FORM and simply being submitted.
I also can't use the jQuery.post() method, which would redirect as required, as I need to be able to set the contentType to "application/json" so my binding works correctly. Unfortunately, the jQuery.post() method doesn't allow you to set this parameter.
I've read that the jQuery.post() method basically uses the jQuery.ajax() method, so I've been battling to get the jQuery.ajax() method to redirect.
I've also read that I can set the default contentType for all jQuery.ajax() methods which would then allow me to use the jQuery.post() method but want to try avoid this if possible.
Thanks
Edit: Updated with Saedeas suggestion:
My JavaScript on the 'Index' View:
<script language="javascript" type="text/javascript">
// Initialize the Shopping Cart object
var m_ShoppingCart = {
UserId: 10,
DeliveryInstructions: "Leave at front desk",
CartItems: []
};
$(document).ready(function () {
$.extend({
postJSON: function (url, data, callback) {
return $.ajax({
type: "POST",
url: url,
data: JSON.stringify(data),
success: callback,
dataType: "json",
contentType: "application/json",
processData: false
});
}
});
});
function PostDataWithRedirect() {
var url = '#Url.Action("ConfirmOrder", "Store")';
$.postJSON(url, m_ShoppingCart, function () { });
}
function AddToCart(id, itemName, price, quantity) {
// Add the item to the shopping cart object
m_ShoppingCart.CartItems.push({
"Id": id,
"ItemName": itemName,
"Price": price.toFixed(2), // Issue here if you don't specify the decimal place
"Quantity": quantity
});
// Render the shopping cart object
RenderShoppingCart();
}
function RenderShoppingCart() {
$("#CartItemList").html("");
var totalAmount = 0;
$.each(m_ShoppingCart.CartItems, function (index, cartItem) {
var itemTotal = Number(cartItem.Price) * Number(cartItem.Quantity);
totalAmount += itemTotal;
$("#CartItemList").append("<li>" + cartItem.ItemName + " - $" + itemTotal.toFixed(2) + "</li>");
});
$("#TotalAmount").html("$" + totalAmount.toFixed(2));
}
</script>
And then the Controller Action 'ConfirmOrder'
[HttpPost]
public ActionResult ConfirmOrder(ShoppingCartModel model)
{
return View(model);
}
So when the PostDataWithRedirect() JavaScript method is called it must hit the ConfirmOrder Controller Action and be redirected to the ConfirmOrder View. The Shopping Cart object on my Index view is built up entirely in JavaScript and the user then clicks a 'Proceed to Checkout' button and is redirected etc.
PS: My full working example can be found in an the article [ How to POST a JSON object in MVC ], I just need to update this code so that it can do the post and redirect as explained above
In the success method, do a location.href = "TARGET LOCATION" to cause the page to go to TARGET LOCATION.
$.ajax({
url: myurl,data,
data: myData,
// processData: false, // you may need this option depending on service setup
success: function(){
location.href = "TARGET LOCATION";
},
type: "POST",
contentType: "application/json"
});
Return a view that redirects:
controller
return View("redirectionView");
view
RedirectionView.cshtml
#{
Layout = null;
}
<script type="text/javascript">
alert("Success! Redirecting...");
window.location = "./";
</script>
EDIT
To accommodate data retention use tempdata.
controller
TempData["collectedUserData"] = collectedData;
return View("redirectionView");
RedirectionView.cshtml
#{
Layout = null;
}
<script type="text/javascript">
alert("Success! Redirecting...");
window.location = "./Rebuilder/ActionMethod";
</script>
Controller Rebuilder
public ActionResult ActionMethod()
{
if( TempData.ContainsKey("collectedUserData") )
{
var collectedData = TempData["collectedUserData"];
}
//todo: use else clause to catch data not present
use collectedData to build new view
return View();
}
Maybe I'm not following the question, but why does the classic post, redirect, get pattern not work?
[HttpGet]
public ActionResult WhateverActionName()
{
YourViewModel yvm = new YourViewModel();
//Initalize viewmodel here
Return view(yvm);
}
[HttpPost]
public ActionResult WhateverActionName(YourViewModel yvm)
{
if (ModelState.IsValid) {
RedirectToAction("OtherAction", "OtherController")
}
return View(yvm);
}
Do you mean you want to do a post, have it bind to the viewmodel for validation purposes in one controller, and then have that controller post to another controller that verifies everything? If so, I suggest taking a look at this code to post in c# http://geekswithblogs.net/rakker/archive/2006/04/21/76044.aspx.
Edit: Or you could just place the data in a tempdata object if the other controller action doesn't truly require a post.
Edit 2: Wait, is the question how to do the post with jquery? If so,
$("#YourForm").submit(function () {
$.post('#Url.Action("WhateverActionName", "YourController")';
});
});
Note: may be small syntax errors, but close to that. The #Url.Action is the important part.
Edit 3: This should finally work.
jQuery.extend({
postJSON: function(url, data, callback) {
return jQuery.ajax({
type: "POST",
url: url,
data: JSON.stringify(data),
success: callback,
dataType: "json",
contentType: "application/json",
processData: false
});
}
});
Call it in your view as
$.postJSON('#URL.Action("Action", "Controller")', yourJson, callback)
And then redirect inside the controller.
I have such autocomplete code:
$("input#PickupSpot").autocomplete({
source: function(request, response){
$.ajax({
url: "AjaxSearch.aspx",
dataType: "jsonp",
data: {
a: "getspots",
c: "updateSpotList",
q: request.term
},
success: function(){
alert("Success");
},
error: function (xhr, ajaxOptions, thrownError){
alert(xhr.status);
alert(thrownError);
}
});
}
});
When i try to get data Firebug shows an error: "updateSpotList is not defined".
I need to creat a function called updateSpotList to get response from the server. And the success alert is never invoked.
Why do i need this function? Maybe there are something defined in aspx? It is:
string response = "";
string callback = Request.QueryString["c"];
string action = Request.QueryString["a"];
string query = Request.QueryString["q"];
//Ensure action parameter is set
if(action != null && action.Equals("getspots")){
//Ensure query parameter is set
if (query != null && query.Length > 0){
SpotListRequest request = new SpotListRequest
{
FreeText = query,
Language = "sv-SE"
};
IEnumerable<Spot> spots = DataBridge.GetSpotList(null, null, query).OrderBy(i => i.FullName);
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(spots.ToArray());
//Ensure callback parameter is set
if (callback != null && callback.Length > 0)
{
response = String.Format("{0}('{1}')", callback, json);
}
}
}
You can specify a URL as the source parameter, instead of the data parameter.
http://jqueryui.com/demos/autocomplete/#option-source
The plugin will make a request to your server, and you should return a JSON array looking like this:
[{label: "Name"}, {label: "Name 2"}]
Change your JavaScript code to this:
$("input#PickupSpot").autocomplete({
source: "AjaxSearch.aspx?a=getspots&c=updateSpotList"
});
The autocomplete plugin will append a parameter named term to the source URL, with the current value of the input element, so a request would be made to: "AjaxSearch.aspx?a=getspots&c=updateSpotList&term=test" if you wrote test in the input element.
On the server, you want to change q to term.
I have a test web service called: MySimpleService.svc with a method called:GetUserNamesByInitials. below is the Linq to SQL code:
[OperationContract]
public static string GetUserNamesByInitials(string initials)
{
string result = "";
//obtain the data source
var testDB = new TestDBDataContext();
//create the query
var query = from c in testDB.TestTables
where c.initials == initials
select c;
//execute the query
foreach (var c in query)
{
result = result + c.full_name;
}
return result;
}
I have used that code in page behind and it works well. However when i try to call it using jQuery I don't get any result, no errors nothing. below is the code i use in jQuery:
$('#TextBox3').live('keydown', function(e) {
var keyCode = e.keyCode || e.which;
if (keyCode == 9) {
e.preventDefault();
//call function here
$.ajax({
type: "POST",
url: "/MySimpleService.svc/GetUserNamesByInitials",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: '{"word":"' + $('#TextBox3').val() + '"}',
success: function(data) {
$('#TextBox4').val(data.d);
}
});
}
});
});
what I do is to type the user id in one textbox (TextBox3) and when I press the Tab key the result is shown in another textbox (TextBox4).
The jQuery call works well with other methods that do not call the database, for example using this other web service method it works:
[OperationContract]
public string ParameterizedConnectionTest(string word)
{
return string.Format("You entered the word: {0}", word);
}
However with the Linq method it just does not work. Maybe i am doing something wrong? thank you in advance.
The web service is expecting a string named "initials" and you're sending it a string called "word". More than likely it is throwing an exception that it's not being passed the expected parameter.
Try adding an error: function() in your $.ajax() call to catch the error. I would also suggest installing FireBug or Fiddler to be able to view and debug the AJAX request and response. Here's the updated $.ajax() call with the right parameter name and an error function which should invoke your chosen javascript debugger if you have it open:
$.ajax({
type: "POST",
url: "/MySimpleService.svc/GetUserNamesByInitials",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: '{"initials":"' + $('#TextBox3').val() + '"}',
success: function(data) {
$('#TextBox4').val(data.d);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert('Error!');
debugger; // Will let you see the response in FireBug, IE8 debugger, etc.
}
});
Update
So I just noticed that you're using WCF services instead of normal ASP.Net (ASMX) services... Some additional suggestions:
Enable IncludeExceptionDetailInFaults in your web.config so that you can debug any errors happening. ASP.Net services will return an error by default but you need to explicitly turn this on for WCF.
Try adding an additional attribute below your [OperationContract] attribute to specify you're expecting a post with JSON data:
[WebInvoke(Method = "POST",BodyStyle=WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)]