Passing String into an API method with a Route Attribute - c#

I have a method on an API Controller where I've set the route in an attribute, but I don't seem to be able to pass a string into this. When I try to hit this with an Ajax Request from the browser the console shows the following error:
BAD REQUEST - The request could not be processed by the server due to
invalid syntax.
The string I'm passing over is huge, but unfortunately is the only way I can import the data into the legacy application I'm working with. The test URL I'm using is (brace yourselves):
http://localhost:50915/api/job/import/ALMIG&sup3123456&sup32%20DAY%20ECONOMY&sup320170720&sup320170721&sup30&sup3&sup3&sup3&sup322&sup3Lara%20Croft%20Way&sup3Derby&sup3&sup3&sup3DE23%206GB&sup3Stuff&sup310&sup31&sup30&sup325&sup30&sup3&sup31%7CI%20Great%20Danger&sup30&sup30&sup30&sup3&sup3&sup30&sup3true&sup30&sup3&sup3&sup3&sup3&sup3&sup3&sup31&sup30&sup30&sup316&sup3Baden%20Road&sup3Stoke-on-Trent&sup3&sup3&sup3ST6%201SA&sup3&sup30&sup30&sup30&sup3&sup3&sup3&sup30&sup30&sup30&sup30&sup3&sup30&sup31&sup30&sup3&sup3&sup3&sup3&sup3&sup3&sup3&sup3&sup3&sup3&sup3Liane&sup307730044916&sup3Lara&sup307730044916&sup30&sup3d2f0acf7-50e1-4a53-96ce-4fffd00b1a96&sup30
And the method is defined as below, the code inside is irrelevant as I put a break point on the start of the method which is never hit:
[System.Web.Http.HttpPost]
[System.Web.Http.Route("api/job/import")]
public int TmsImport([FromBody]string import)
{
// do something...
}
Edit: Added Ajax Request
job.confirmBookings = function () {
// TMS Import
job.toConfirmRow.filter(function(obj) {
var jobRow = obj;
var strArray = [];
for (var prop in jobRow) {
if (jobRow.hasOwnProperty(prop)) {
strArray.push(jobRow[prop]);
}
}
var joinedStr = strArray.join(job.seperator);
$.ajax({
type: "POST",
crossDomain: true,
data: joinedStr,
url: job.tmsString,
contentType: "application/json;charset=utf-8",
success: function (data, status, xhr) {
console.log("TMS ID: " + data + " | " + status);
},
error: function (xhr) {
alert(xhr.responseText);
}
});
});

First format the route template correctly
[HttpPost]
[Route("api/job/import")] //Matches POST api/job/import
public int TmsImport([FromBody]string import) {
// do something...
}
Also you should post the data in the body of the request. If the payload is large then you do not want that in the URL

Related

Asp .net core project: Ajax call can't reach controller (400 status code)

I'm refactoring my ASP .net core project and I want to make use of some Ajax calls, but I'm facing the following problem: when I try to send a request to a method in a controller of my project but it doesn't work.
I have a modal with the following button:
<button onclick="reviewRestaurant(#item.RestaurantId)" class="btn btn-outline-primary">Add review</button>
the reviewRestaurant() function looks like this:
function reviewRestaurant(restaurantId) {
let rating = $("input[type=radio]:checked").val();
let review = $('textarea').val();
let data = { restaurantId, rating, review };
$.ajax({
type: "POST",
url: `/Restaurants/Review`,
data: JSON.stringify(data),
success: function (res) {
// TODO: work on this later
},
error: function (err) {
console.log(err);
}
});
}
And the method that I wanna call in the Restaurants controller looks like so:
[Authorize]
[HttpPost]
public async Task<IActionResult> Review(int restaurantId, string rating, string content)
{
var username = this.User.Identity.Name;
await this.restaurantsService.Review(restaurantId, username, rating, content);
return this.RedirectToAction("Details", new { id=restaurantID });
// the output of this method will be refactored later once I manage to get to it
}
The problem is I cannot reach the Review method in the controller. I get the 400 status code and I don't know how to fix it. I tried using DTO and [FormData] attribute for the method parameters and tried passing the data without stringifying it still nothing works.
If anyone can help me I would be very grateful. I'm relatively new to ajax calls and simply cannot see where my mistake is.
You can try this solution:
Create a common class for your parameters (int restaurantId, string rating, string content) and use FromBody. => Review([FromBody] YourClassName)
And write this parameters in your ajax code.
contentType: 'application/json; charset=utf-8',
data: 'json'
I hope it will help you.
Here is a working demo which I test.
#section Scripts
{
<script>
function reviewRestaurant(restaurantId) {
let rating = $("input[type=radio]:checked").val();
let content = $('textarea').val();
let data = { restaurantId, rating, content };
$.ajax({
type: "POST",
url:'/Restaurants/Review',
data: data,
success: function (res) {
// TODO: work on this later
},
error: function (err) {
console.log(err);
}
});
}
</script>
}

Finding it impossible to post simple ajax

I am realy struggling with this and would apprecate any advice.
I have this field
<input id="scanInput" type="text" placeholder="SCAN" class="form-control" />
For which I would like to make an ajax call when the field changes, so I tried
<script>
$("#scanInput").change(function () {
console.log('ye');
$.getJSON("?handler=GetPartDetails", function (data) {
//Do something with the data.
console.log('yay?');
}).fail(function (jqxhr, textStatus, error) {
var err = textStatus + ", " + error;
console.log("Request Failed: " + err);
});
});
</script>
Where my PageModel has this method
[HttpPost]
public IActionResult GetPartDetails()
{
return new JsonResult("hello");
}
and the url for the page in question is /packing/package/{id}
Now when I change the input value, I see ye on the console, and I can see that the network called http://localhost:7601/Packing/Package/40?handler=GetPartDetails (the correct URL I think?) with status code 200
But My breakpoint in GetPartDetails never hits, and I don't see yay? in the console.
I also see this message from the fail handler:
Request Failed: parsererror, SyntaxError: JSON.parse: unexpected character at line 3 column 1 of the JSON data
But I'm not even passing any JSON data... why must it do this
I also tried this way :
$.ajax({
type: "POST",
url: "?handler=GetPartDetails",
contentType : "application/json",
dataType: "json"
})
but I get
XML Parsing Error: no element found
Location: http://localhost:7601/Packing/Package/40?handler=GetPartDetails
Line Number 1, Column 1:
I also tried
$.ajax({
url: '/?handler=Filter',
data: {
data: "input"
},
error: function (ts) { alert(ts.responseText) }
})
.done(function (result) {
console.log('done')
}).fail(function (data) {
console.log('fail')
});
with Action
public JsonResult OnGetFilter(string data)
{
return new JsonResult("result");
}
but here I see the result text in the console but my breakpoint never hits the action and there are no network errors..............
What am I doing wrong?
Excuse me for posting this answer, I'd rather do this in the comment section, but I don't have the privilege yet.
Shouldn't your PageModel look like this ?
[HttpPost]
public IActionResult GetPartDetails() {
return new JsonResult {
Text = "text", Value = "value"
};
}
Somehow I found a setup that works but I have no idea why..
PageModel
[HttpGet]
public IActionResult OnGetPart(string input)
{
var bellNumber = input.Split('_')[1];
var partDetail = _context.Parts.FirstOrDefault(p => p.BellNumber == bellNumber);
return new JsonResult(partDetail);
}
Razor Page
$.ajax({
type: 'GET',
url: "/Packing/Package/" + #Model.Package.Id,
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
input: barcode,
handler: 'Part'
},
success: function (datas) {
console.log('success');
$('#partDescription').html(datas.description);
}
});
For this issue, it is related with the Razor page handler. For default handler routing.
Handler methods for HTTP verbs ("unnamed" handler methods) follow a
convention: On[Async] (appending Async is optional but
recommended for async methods).
For your original post, to make it work with steps below:
Change GetPartDetails to OnGetPartDetails which handler is PartDetails and http verb is Get.
Remove [HttpPost] which already define the Get verb by OnGet.
Client Request
#section Scripts{
<script type="text/javascript">
$(document).ready(function(){
$("#scanInput").change(function () {
console.log('ye');
$.getJSON("?handler=PartDetails", function (data) {
//Do something with the data.
console.log('yay?');
}).fail(function (jqxhr, textStatus, error) {
var err = textStatus + ", " + error;
console.log("Request Failed: " + err);
});
});
});
</script>
}
You also could custom above rule by follow the above link to replace the default page app model provider

jQuery AJAX always executing error: {}

I'm working on a webapplication, ASP.Net MVC 4.0 with entityframework 6.0, trying to update database as per user selection. Data is sent to controller's action using jQuery AJAX. Below given is C# code to update entity which in turn updates database.
public void modidyProduct(Productdetail prodData)
{
try
{
using (SampleTrialEntities entity = new SampleTrialEntities())
{
var data = entity.Productdetails.Where(p=>p.ProductID == prodData.ProductID).FirstOrDefault<Productdetail>();
data.ProductName = prodData.ProductName;
data.ProductNumber = prodData.ProductNumber;
data.CategoryName = prodData.CategoryName;
data.ModelName = prodData.ModelName;
entity.Entry(data).State = System.Data.Entity.EntityState.Modified;
entity.SaveChanges();
}
}
catch (Exception)
{}
}
And here's jQuery AJAX call for that controller action method.
function updateProduct() {
var productData = {
ProductName: $('#prodName').val().trim(),
ProductNumber: $('#prodNum').val().trim(),
CategoryName: $('#ctgryName :selected').text(),
ModelName: $('#mdlName :selected').text(),
ProductID: atob($('#editProductId').val())
};
debugger;
$('#divLoader').show();
$.ajax({
url: '#Url.Action("modidyProduct", "Home")',
data: JSON.stringify(productData),
type: 'POST',
dataType: 'json',
contentType: 'application/json;charset=utf-8',
success: function (jqXHR) {
//Below line will destroy DataTable - tblProducts. So that we could bind table again. next line - loadData();
$('#tblProducts').DataTable().destroy();
$('#divLoader').hide();
loadData();
$('#addModal').modal('hide');
$('#editProductId').val('');
},
error: function (msg) {
debugger;
$('#editProductId').val('');
$('#divLoader').hide();
alert(msg);
alert("What's going wrong ?");
//alert(jqXHR.responseText);
}
});
}
After executing jQuery AJAX method & controllers action, successfully updates the record in database. Response statuscode - 200 & Status - OK is returned. But only error: { }, code block is executed every time in AJAX method.
Debugging screen capture with status-OK; statuscode - 200
This part of your $.ajax method call
dataType: 'json',
It tells jQuery that, the ajax call code is expecting a valid JSON response back but currently your server method's return type is void. That means it won't return anything and the $.ajax method is trying to parse the response (assuming it is a valid JSON), and hence getting the typical "parsererror"
When the datatype is json and the response is received from the server, the data is parsed in a strict manner; any malformed JSON is rejected and a parse error is thrown. As of jQuery 1.9, an empty response is also rejected.
The solution is to simply remove the dataType property in the call.
$.ajax({
url: '#Url.Action("modidyProduct", "Home")',
data: JSON.stringify(productData),
type: 'POST',
contentType: 'application/json;charset=utf-8'
}).done(function() {
console.log('Success');
})
.fail(function(e, s, t) {
console.log('Failed');
});
Or you can update your server action method to return a json response.
[HttpPost]
public ActionResult ModidyProduct(Productdetail prodData)
{
try
{
//to do : Save
}
catch (Exception ex)
{
//to do : Log the exception
return Json(new { status = "error", message=ex.Message });
}
return Json(new { status="success"});
}
Now in your client side code, you can check the json response to see if the transaction was successful
$.ajax({
url: '#Url.Action("ModidyProduct", "Home")',
data: JSON.stringify(productData),
type: 'POST',
contentType: 'application/json;charset=utf-8',
dataType: 'json',
}).done(function (res) {
if (res.status === 'success') {
alert('success');
} else {
alert(res.message);
}
console.log('Success');
}).fail(function(e, s, t) {
console.log('Failed');
});
You do not need to necessarily specify the dataType property value. If nothing is specified jQuery will try to infer it based on the mime type of the response coming back, in this case, the response content type will be application/json; charset=utf-8. So you should be good.

How to obtain checked checkbox values on the serverside in c# from an ajax Http POST using web forms (not MVC)?

Here's my ajax call:
$(function () {
$("#chkFilter").on("click", "input", function (e)
{
var filterCheckboxes = new Array();
$("#chkFilter").find("input:checked").each(function () {
//console.log($(this).val()); //works fine
filterCheckboxes.push($(this).prop("name") + "=" + $(this).val());
console.log($(this).prop("name") + "=" + $(this).val());
//var filterCheckboxes = new Array();
//for (var i = 0; i < e.length; i++) {
// if (e[i].checked)
// filterCheckboxes.push(e[i].value);
//}
});
console.log("calling ajax");
$.ajax({
url: "/tools/oppy/Default",
type: "POST",
dataType: "json",
data: { filterValues: filterCheckboxes }, // using the parameter name
success: function (result) {
if (result.success) {
}
else {
}
}
});
});
});
And my server side code:
public partial class tools_oppy_Default : System.Web.UI.Page
{
...
protected void Page_Load(object sender, EventArgs e)
{
if (Request.HttpMethod == "POST")
{
string checkedBoxes = Request["filterValues"];
testLabel.Text = checkedBoxes;
}
I'm just trying to obtain the post URL with the appropriate checked values so I can parse it on the server. However, I'm having trouble obtaining the URL. The string checkedBoxes is supposed to hold a query string like name=value&name=value&name.... but when I test it, the testLabel doesn't show anything. I'm using web forms app, not MVC. Also, I'm new to ajax and their behavior. Thanks.
First, I assume that the url in you JQuery call is valid as there is not aspx extension their.
Second, It looks like what you need to do is create a web method and call it from JQuery for example the following is a web method that accept string
[WebMethod]
public static string GetData(String input)
{
return DateTime.Now.ToString();
}
and you can call it using the same way with your current code just update the url parameter to include the method name
url: "PageName.aspx/MethodName",
for more details about web methods and their union with JQuery please check this article
Edited The following is complete sample
The web method should look like the following one
[WebMethod]
public static string GetData(string filterValues)
{
return filterValues; //This should be updated to return whatever value you need
}
The client side part of calling the web method should look like the following
$.ajax({
url: "/Default/GetData",
type: "POST",
contentType: "application/json; charset=utf-8", //I have added this as "contentType" parameter represents the type of data inside the request meanwhile the "data" parameter describes the data inside the response
data: "{ filterValues:\"" + filterCheckboxes + "\"}", //Note that I have updated the string here also set the name of the parameter similar to the input of the webmethod
dataType: "json",
success: function (result) {
alert(result.d);//You should access the data using the ".d"
}
});
One last thing, If you are using asp.net permanent routing the above code will not work and you should disable it by updating the file "App_Code/RouteConfig.cs" From
settings.AutoRedirectMode = RedirectMode.Permanent;
To
settings.AutoRedirectMode = RedirectMode.Off;
And remember to clear browser cache after the above update

web api put is recognizing query strings but not body

when i pass in users as a query string (using params in $http) and set the web api method to look for them in the uri everything is peachy. but when i pass them in as below, users shows up as null. what am i missing here?
angular function
scope.saveChanges = function () {
// create array of user id's
var users = [];
angular.forEach(scope.usersInRole, function (v, k) {
users.push(v.Key);
});
var data = { user: users };
var token = angular.element("input[name='__RequestVerificationToken']").val();
// put changes on server
http({
url: config.root + 'api/Roles/' + scope.selectedRole + '/Users',
method: 'PUT',
data: data,
contentType: "application/json; charset=utf-8",
headers: { "X-XSRF-Token": token },
xsrfCookieName: '__RequestVerificationToken'
}).success(function (result) {
// notify user changes were saved
angular.element('#myModal').reveal({ closeOnBackgroundClick: false });
});
};
web api action
public HttpResponseMessage Put(HttpRequestMessage request, [FromUri]string role, [FromBody]string[] user)
{
return request.CreateResponse(HttpStatusCode.NoContent);
}
Try: data: users instead of data: data.
In asp.net api, the whole request body is bound to a parameter. For this reason, you cannot have multiple parameters with the [FromBody] in the action method parameters. There is only one => we don't need to specify a property name in the request body.

Categories

Resources