I'm new to MVC3 framework (and .NET overall; Java veteran), so bear with me, but here goes:
Input submitted to a Controller as JSON doesn't seem to be subject to the HttpRequestValidation -- Does that sound right?
I realize if you're receiving data input via JSON you're possibly already doing more work with it, but the Controller Action doesn't seem to necessarily know whether it has JSON data at that point; input values are mapped to parameters just as they would be if they were standard POST params.
Example - I'm asynchronously submitting JSON data to my Controller like the following:
var data = { "title": $titleField.val(), "content": $textArea.val(),
"location": $location.val()
};
$.ajax(submitUrl,
{
type: "POST",
contentType: "application/json; charset=utf-8",
complete: function (data) {
//blah blah
},
dataType: 'json',
data: JSON.stringify(data)
});
}
I then receive the input in my Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult New(string title = "", string content = "", string location = "")
{
//yada yada
}
Doing this, params are mapped and the user can easily send tags, etc. I'm not turning ValidateInput off, and if I submit with a standard POST and remove the Stringify, it throws the error as expected. Any good reason why JSONified data would skip validation?
Edit - More specific question: If JSONified data will pass HttpRequestValidation, how can we protect against the event where someone would intentionally mock a request to send JSON data instead of post params? I haven't found a way to force the Action method to differentiate between params passed as JSON vs. those passed non-encoded.
Got an answer for my question over on asp.net - See 2nd response.
Solution involves replacing the default ModelBinder.
Any good reason why JSONified data would skip validation?
JSON is encoded => so it ensures that what transits over the wire is safe. When you use JSON.stringify all dangerous characters are encoded.
Related
I have a form which includes a variety of <input> elements and makes use of 1-to-n tabulator tables for data input. I have managed to successfully assemble data from these elements into a JSON string. I am now attempting to complete the following two steps:
Using Ajax, post the JSON object to my web server; and
In the ASP.NET MVC controller, upload the deserialized JSON data into a SQL Server 2016 table.
My client-side script to POST the JSON object is as follows:
var myJson = "the data from the form elements is programmatically inserted into the JSON string";
// use parseJSON() to test the syntax
try {
var obj = jQuery.parseJSON(myJson);
}
catch(error) {
console.log(error);
}
$.ajax({
type: "POST",
url: "/Dailies/UploadJson/",
dataType: 'json',
data: JSON.stringify(myJson),
contentType: 'application/json',
crossDomain: true,
cache: false,
success: function(data) { console.log(data); }
});
The method called within my ASP.NET MVC controller appears as follows:
[HttpPost]
public IActionResult UploadJson(Object jsonFile)
{
// insert data into SQL Server table
}
Note: I have already created the appropriate domain model within my ASP.NET MVC app and have also added a DbSet reference to the DbContext model. I have verified my ability to insert rows into the SQL Server table using mock data.
When I place a breakpoint inside the UploadJson() method, I find that the jsonFile object is null.
My quandry at this point is two-fold:
I can't seem to get JSON data from the client to the web server; and
I need to better understand how to transform the JSON data (once received) for upload into my database.
Any assistance is greatly appreciated.
Although there are plenty of questions related to this, the answers to those typically refer to binding to a model instead of just the json string. But those will also help you.
It looks like there are two things:
I would change the controller to receive a string instead of an object.
You'll need to update the json data you're passing to the controller to match the parameter name of the controller. So in this case, the controller would receive a parameter named jsonFile. So in the $.ajax method you'll want update the data to something like:
data: { jsonFile: JSON.stringify(myJson) }
UPDATE:
remove the Content-Type of application/json
Am very new to angularjs and i need to post data to a web service, the service accepts two parameters, one is List of object and the other is securityToken,
Here is my code,
$scope.saveCompany=function(){
// alert("Save Company!!!");
var Companies={
Code: 'testMartin',
Name: 'company1',
CompanyType : 'Tenant',
email : 'test#yaoo.com',
Fax : 4235353,
ParentID : 1
};
$http({
url:'http://localhost/masters/smstools.svc/json/SaveComapnies',
dataType: 'json',
method: 'POST',
data: $.param(Companies),
headers: {
"Content-Type": "text/json",
}
}).success(function(response){
alert ("Success");
}).error(function(error){
alert ("Save company!");
});
how can i pass the security token with the companies object as a separate paramenter. my service accepts the parameters like this,
public List<CompanyProfile> Save(List<CompanyProfile> CompanyList,string securityToken)
Since this is a rest call you only have 3 places were you can pass parameters data:
With Post and it will be part of the body, it seems this is what is your first parameter is occupying now.
With Get and you add the parameter to the URL /json/SaveComapnies/mySecParam or by queryString like /json/SaveComapnies?sec=mySecParam but this is not secure nor recommended for security settings.
With header from angular Post:
**headers: {
"Content-Type": "text/json",
"mySecVar": "mySecParamValue"
}**
Server side version:
public List<CompanyProfile> Save(List<CompanyProfile> CompanyList){
WebOperationContext current = WebOperationContext.Current;
WebHeaderCollection headers = current.IncomingRequest.Headers;
if (headers["mySecVar"] != null){
// do something
}
}
You can read more about it here:
How to read HTTP request headers in a WCF web service?
Can you share more information in your Backend?
If it is actually a REST Backend I would rather use an angular $resource
https://docs.angularjs.org/api/ngResource
If you want to pass json object and string as post Parameter you should stick to the $http docs
https://docs.angularjs.org/api/ng/service/$http
In the post example you can pass both params in:
$http.post('/yourEndpoint', {jsonObj:yourCompaniesObj, secKey:yourSecretToken})....(sucess etc)
Typing from my cell - if you need more code examples just tell
There are plenty of tutorials out there showing how to build a form with a simple POJO model object and the "command" property on the Spring taglib.
Examples: A tutorial
I seem to thrive on making my life hard, though and have my form running in a jQuery dialog box...whose checkbox statuses are pre-processed with a javascript function. I'm doing this through AJAX calls in that javascript function.
Sooo, my form init looks like this:
<form:form action="javascript:associateArtifacts()">
This invoked my javascript method:
function associateArtifacts(){
/* JSONIFY the list of checked boxes and send that data to the server here in an AJAX call */
var artifacts = [];
$('#artifacts_container input:checkbox').each( function(j,listitem) {
artifacts.push(listitem.name);
});
$.ajax({
type: "POST",
url: "associateArtifacts.html",
data:JSON.stringify(artifacts),
success: function(html){
alert( "Submitted");
}
});
return false;
}
The point of this is building a dialog with a list of checkboxes that are checked based on DB data that the user can modify then save back to the server.
My issue is, I'm getting the dialog up and populated with the checkboxes and loaded with the values, but I seem to have not thought it through as I have no idea how to intercept the data coming to the server as JSON and process it.
In the past I've done this with URL'd parameters, but in this case is a variably-sized large string of JSON data.
Am I going to be forced to define an object that just hold a single List in it for my Spring MVC container to assign this JSON data to? Seems kind of ridiculous...
Thoughts? Suggestions? Harsh criticisms for going at this entirely wrong?
Thanks for any of the above.
EDIT
Signature looks like this:
#RequestMapping(value = "/associateArtifacts.html", method = RequestMethod.POST, headers = {"content-type=application/json"})
public void associateArtifacts(#RequestBody List<String> checkboxData){
Client from data in Chrome Tools looks like this:
["checkboxTitle1","checkboxTitle2","checkboxTitle3"]:
Which is produced by my client when I take my array of checkBox names and do this to it:
JSON.stringify(arrayOfNames)
Current error is:
HTTPStatus 415 - The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
EDIT #2
It was the lack of contentType on my AJAX call, as you suggested. Thanks.
Assuming your JSON is something like this:
"[{checkbox1: 'true'}, {checkbox2: 'false'}]"
Firstly include jackson-mapper-asl on your classpath:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
Post using JQuery ajax like this:
$.ajax({
type: "POST",
url: "associateArtifacts.html",
data: JSON.stringify(artifacts),
contentType: 'application/json',
success: function(html) {
alert("Submitted");
}
});
And define your Spring MVC controller handler method like this:
#RequestMapping(value = "/associateArtifacts.html", method = RequestMethod.POST, headers = {"Content-type=application/json"})
#ResponseBody
public MyResponse associateArtifacts(#RequestBody List<Map<String,String>> checkboxData) {
// .. process the data here
}
The JSON string will be automatically bound into a list of map (key value pair).
The #ResponseBody annotation will cause the returned MyResponse object to be serialized into JSON
I am trying to convert a regular old controller I was using to an API controller and am having a little bit of difficulty. What these series of functions do is, in the jQuery, it iterates over a file containing all the usernames of employees and for each username it makes a call to the PopulateEmployee method in my webapi controller which should return JSON and then populate a results div.
When manually navigating to
..domain../staffinformation/populateemployee/employeeusername
I get the error
This XML file does not appear to have any style information associated with it. The
document tree is shown below.
<Error>
<Message>
The requested resource does not support http method 'GET'.
</Message>
</Error>
Please note that the div it will be populating is a partial view in an Umbraco CMS page and I don't think that is the problem but if you guys think differently please tell me.
There has to be something I am missing either with webAPI routing or something else.
Thanks for your help.
Here's the codez.
Please notice that this method has the HttpPost tag
public class StaffInformationController : ApiController
{
[System.Web.Http.ActionName("PopulateEmployee")]
[System.Web.Http.HttpPost]
public StaffListing PopulateEmployee(string id)
{
//do error checking on input
StaffListing staffListing = new StaffListing(id);
//populate other fields
return staffListing;
}
}
The routing set up for the api controller
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
The jQuery call specifying use of 'POST', please forgive the trickiness of the recursive call in this function.
function getEmployeeObjectByIndex() {
$.ajax({
url: $('#root').val() + '/api/StaffInformation/PopulateEmployee',
type: 'POST',
async: true,
contentType: 'application/json, charset=utf-8',
data: JSON.stringify({ 'username': lines[i] }),
success: function (staffObject) {
if (!(staffObject.Name == undefined)) {
buildHtmlStrings(staffObject);
}
i++;
getEmployeeObjectByIndex(); //recursive call
}
});
}
manually navigating to that address throws the error because, when manually navigating you are doing a GET (and your method only allows POSTs).
You should fire up Fiddler and watch the ajax POST request and response to see how the server is responding / your request is being made
Jquery ------> web api
Web API has one property i.e. CONTENT NEGOTIATION means you send any data and accept any data as you want.
$.ajax({
contentType: 'application/json, charset=utf-8',
// this is sending your data of datatype json to server, here you send any type of data
accept: 'application/json',
//this is receiving/getting data form server to client...
// SO HERE YOU GET JSON DATA AS YOU WANT only mention which data of datatype u want...
//if you sending xml and you want json so only write accept as json it get automatically converted into your required datatype..by MediaTypeFormatter
});
I can successfully make a jQuery Ajax call into my C# Controller and receive back an XML string, but I need to in turn gather some Portfolio dates and package them up into a JSON object so I can send them back into another C# Controller.
If it's a C# issue, then I apologize if I'm in the wrong forum...however I'd like to pass my JSON object into the server side controller ..
Here's what I'm trying to do:
var nodeDatesJson = {"nodedates": // CREATE JSON OBJECT OF DATE STRINGS
{ "date": 01/20/2012,
"date": "01/21/2012" } };
getTradeContribs(thisPfId, nodeDatesJson.nodedates.date);
Now call the next js function:
function getTradeContribs(pfid, nodedates) {
//alert(nodedates);
$.ajax({ // GET TRADE CONTRIBS FROM SERVER !!
url: "/Portfolios/getTradeContribs?portfolioId=" + pfid + "&nodedates=" + nodedates,
type: "GET", // or "PUT"
dataType: "json",
async: true,
success: parseTradeContribs,
error: function (error) {
alert("failed in opening Trade Contribs file !!!");
}
});
}
function parseTradeContribs(data) {
alert("In parseTradeContribs..." );
$(data).find("Trade").each(function(){
$(".TradeContrib").append($(this).text());
})
}
and my C# controller is trying to read in the "nodedates" JSON object, but HOW do I read it in ?
public string getTradeContribs(string portfolioId, **string nodedates**)
{
// Build Portfolio Select request here !
RequestBuilder rzrRequest = new RequestBuilder();
// REQUEST FOR CONTRIBUTIONS !
// ... more code here..
xmlResponse.LoadXml(contribResponse);
string jsonTest = #" {""nodedates"": ""date"":""01/01/2012""}";
//return xmlResponse.OuterXml; // WORKS FINE
return "<Trade><TradeId>1234</TradeId></Trade>"; // RETURN TEST XML STR
}
thank you in advance...
Bob
The best way to receive a list of dates in a MVC action is to bind to a collection. What this means is that you should put your dates and other attributes in a form with the following naming convention:
<input type="hidden" name="dates" value="2012-1-20" />
<input type="hidden" name="dates" value="2012-1-21" />
Then you should serialize this form (look into jquery's docs for this) and post its data to your action, which will be something along the lines of:
public ActionResult getTradeContribs(string portfolioId, IList<DateTime> dates) {
// Do your work here
}
You should really take a look into MVC Model binding and collection binding as well:
Model binding to a list
Model binding objects
Also, if I may, your javascript object has two properties with the same name, which is probably not what you mean. If you want to have multiple dates stored somewhere in a object, you should use an array:
var nodeDatesJson = {"nodedates":
[ "01/20/2012", "01/21/2012" ] };
Sorry, but I didn't understand your doubt very well...but here it goes:
Maybe you should pass the json, well-formatted, as a string and use some C# parser.
This way you can get a object in server-side as same as the Json object in javascript.
=]