I created a custom tool in AEM and I wanted the custom tool to call a servlet(POST json).
The servlet was called but, request.getParameterMap() return empty map.
My servlet code is
#Component(
service=Servlet.class,
property={
Constants.SERVICE_DESCRIPTION + "=Custom Servlet",
"sling.servlet.methods=" + HttpConstants.METHOD_POST,
"sling.servlet.paths=" + "/apps/myapp/customServlet"
}
)
public class CustomServlet extends SlingAllMethodsServlet{
#Override
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws IOException {
String method = request.getMethod(); // method POST OK
Map map = request.getParameterMap(); // return map but empty
String name = request.getParameter("foo"); // also this return null
response.setStatus(SlingHttpServletResponse.SC_OK);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().print("{\"response message\" : \"" + foo + "\"}");
}
}
and my JS code(loaded to AEM custom tool page as a client library)
window.onload = function() {
var url = '/apps/myapp/customServlet';
var button = document.getElementById('btnSubmit');
button.addEventListener('click', event => {
var foo = document.getElementById('foo').value;
if (foo.length < 1){
alert("input required!!");
} else {
var requestData = {};
requestData.foo= foo;
console.log(requestData); // it is ok
postData(url,requestData).then(data => {console.log(data);}); // got {response message:null}
}
});
}
async function postData(url, data){
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
return response.json();
}
In addition, I deleted POST at filter methods in Adobe Granite CSRF Filter Config.
Do I need any other OSGi config or something wrong with my code to use Post Servlet in AEM?
To get payload from request body, you can use request.getReader()
For example:
String body = IOUtils.toString(request.getReader()); //apache commons io
Or use some json mapper to immediately get your java object
YourObject yourObject = new ObjectMapper().readValue(request.getReader(), YourObject.class); // jackson
I'm seeing
var requestData = {}; // creating an empty array
and you are posting this to the servlet.
I think you want to parse 'foo' from document.getElementById to the servlet.
I have a problem whereby I would like to pass a json string as a field value but I keep getting "The input was not valid". So to be clear I have an object in my front end that I use the below to pass to my API:
let j: Settings = {} as Settings;
j.user_settings_ID = object.user_settings_ID;
j.user_ID = object.user_ID;
j.user_filter = JSON.stringify(object.user_filter);
j.user_test_filter = JSON.stringify(object.user_test_filter);
fetch('api/Profile/UpdateProfileSettings/?id=' + object.user_settings_ID, {
method: 'put',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Authorization': 'Bearer ' + sessionStorage.getItem('token')
},
body: "'" + JSON.stringify(j) + "'",
}).then(data => {
if (!data.ok) {
alert("Failed");
}
}).catch((ex) => {
alert("Failed");
});
In my API I have:
[HttpPut("[action]")]
public async Task<string> UpdateProfileSettings(int id, [FromBody] string obj)
{
HttpClient clientRoute = new HttpClient();
var response = await clientRoute.PutAsync("https://something.com/api/UserSettings/put/" + id, new StringContent(obj, Encoding.UTF8, "application/json"));
var contents = await response.Content.ReadAsStringAsync();
return contents;
}
I don't have a problem when I set the j.user_filter and j.user_test_filter with any normal string, but I would like to put the 'jsonified' string as the value for the field, but the Web API doesn't like it for some reason (probably because it isn't seeing it as a string but a json object perhaps)
If someone could help I would be most grateful.
Ok after messing about with this for a long time, I came up with this 'solution'.
So as #Niladri pointed out the "'" before JSON.stringify(j) was a factor but was not the only thing that needed to be changed. The main problem was actually in the controller itself.
I had this previously in my controller:
public async Task<string> UpdateProfileSettings(int id,[FromBody] string obj)
{
HttpClient clientRoute = new HttpClient();
var response = await clientRoute.PutAsync("https://something.com/api/UserSettings/put/" + id, new StringContent(obj, Encoding.UTF8, "application/json"));
var contents = await response.Content.ReadAsStringAsync();
return contents;
}
But I had to change it to:
public async Task<string> UpdateProfileSettings(int id,[FromBody] object obj)
{
HttpClient clientRoute = new HttpClient();
var response = await clientRoute.PutAsync("https://something.com/api/UserSettings/put/" + id, new StringContent(obj.ToString(), Encoding.UTF8, "application/json"));
var contents = await response.Content.ReadAsStringAsync();
return contents;
}
Notice the change of [FromBody] string obj to [FromBody] object obj and also
changed StringContent(obj, Encoding.UTF8, "application/json")) to StringContent(obj.ToString(), Encoding.UTF8, "application/json"))
My previous method with "'" before JSON.stringify(j) works if your controller [FromBody] is of string type and you aren't looking to pump a string which looks like JSON into your controller.
I apologise if this is a bad explanation but tried my best and it worked for me
This is very similar to mcc20's own fix, but I didn't get that code to work. The 2.1 framework has Issues https://github.com/aspnet/Mvc/issues/7609 and https://github.com/aspnet/Mvc/issues/7799. I got this JSON post to complex class working in 2.1: client javascript is unchanged:
var payload = JSON.stringify({ "name": document.getElementById('firstname').value, "email": document.getElementById('email').value, "captcha": grecaptcha.getResponse() });
var oReq = new XMLHttpRequest();
oReq.ContentType = "application/json";
oReq.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("update").innerHTML = this.responseText;
}
else document.getElementById("update").innerHTML = "not 4&200! : " + this.responseText;
};
oReq.open('POST', 'api/u');
oReq.send(payload);
and the controller has:
[Route("api/[controller]")]
// [ApiController] // works either way
public class UController : ControllerBase
{
[HttpPost]
public async Task<string> signUp()
{
String body;
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
body = await reader.ReadToEndAsync();
}
UserSignup user = JsonConvert.DeserializeObject<UserSignup>(body);
return user.email;
}
}
I was trying something like ([FromBody] UserSignup user) getting Mvc.SerializableError and "The input was not valid." Soon to be on https://github.com/Hover-Us/
Im very new to javascript,jquery and ajax. So I have a model:
namespace hiophop.Models
{
public class CarMake
{
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public int CategoryID { get; set; }
}
}
}
I create a list from the classes and add to them in my controller while passing JSON to the view:
namespace hiophop.Controllers
{
public class CarController : Controller
{
List<Category> lstCat = new List<Category>()
{
new Category() { CategoryID = 1, CategoryName = "Dairy" },
new Category() { CategoryID = 2, CategoryName = "Meat" },
new Category() { CategoryID = 3, CategoryName = "Vegetable" }
};
List<Product> lstProd = new List<Product>()
{
new Product() { ProductID = 1, ProductName = "Cheese", CategoryID = 1 },
new Product() { ProductID = 2, ProductName = "Milk", CategoryID = 1 },
new Product() { ProductID = 3, ProductName = "Yogurt", CategoryID = 1 },
new Product() { ProductID = 4, ProductName = "Beef", CategoryID = 2 },
new Product() { ProductID = 5, ProductName = "Lamb", CategoryID = 2 },
new Product() { ProductID = 6, ProductName = "Pork", CategoryID = 2 },
new Product() { ProductID = 7, ProductName = "Broccoli", CategoryID = 3 },
new Product() { ProductID = 8, ProductName = "Cabbage", CategoryID = 3 },
new Product() { ProductID = 9, ProductName = "Pepper", CategoryID = 3 }
};
public ActionResult GetCategories()
{
return Json(lstCat, JsonRequestBehavior.AllowGet);
}
public ActionResult GetProducts(int intCatID)
{
var products = lstProd.Where(p => p.CategoryID == intCatID);
return Json(products, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(string c ,string p)
{
ViewBag.x ="Product:"+ p;
ViewBag.y = "category" + c;
return View();
}
}
}
Here is were it gets confusing How do I retrieve the text value of the selected list box I am only able to retrieve the Int index for the CategoryId's. I want CategoryName ProductName both strings. Here is my view: The viewbags.x and y only return Ids. Ive tried a few things but Im stuck am I doing something wrong?I left some commented out for you to see what im trying.
#model hiophop.Models.CarMake
#{
ViewBag.Title = "CarView";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#*<h2>CarView</h2>*#
#ViewBag.y
#ViewBag.x
#using (Html.BeginForm("Index", "Car", FormMethod.Post))
{
<div>
<label for="category">Category</label>
<select id="category" name="c" class="form-control"></select>
<label for="product">Product</label>
<select id="product" name="p" class="form-control"></select>
<div id="result"></div>
<input type="submit" id="Button1" class="btn btn-default" />
</div>
}
#section scripts {
<script type="text/javascript">
$(document).ready(function () {
// Get a list of categories and a list of products of the first category.
$.getJSON('/Car/GetCategories', null, function (data) {
$.each(data, function () {
$('#category').append('<option value=' +
this.CategoryID + '>' + this.CategoryName + '</option>');
});
$.getJSON('/Car/GetProducts', { intCatID: $('#category').val() }, function (data) {
$.each(data, function () {
$('#product').append('<option value=' +
this.ProductID + '>' + this.ProductName + '</option>');
});
}).fail(function (jqXHR, textStatus, errorThrown) {
alert('Error getting products!');
});
}).fail(function (jqXHR, textStatus, errorThrown) {
alert('Error getting categories!');
});
// Dropdown list change event.
$('#category').change(function () {
$('#product option').remove();
$.getJSON('/Car/GetProducts', { intCatID: $('#category').val() }, function (data) {
$.each(data, function () {
$('#product').append('<option value=' +
this.ProductID + '>' + this.ProductName + '</option>');
});
}).fail(function (jqXHR, textStatus, errorThrown) {
alert('Error getting products!');
});
});
});
//var result = $('#result');
//$(document).ready(function () {
$("#Button").click(function () {
var request = $('#category option:selected').text() + "," + $('#product option:selected').text();
$.ajax({
type: 'POST',
contentType: "application/json;charset=utf-8",
url: '/Car/Index',
//data: "{'Category':'" + document.getElementById('#category') + "','food':'" + document.getElementById('#product') + " '}",
// async: false,
// success: function (response) {
// $('#category').val('');
// $('#product').val('');
// alert("record has been saved in database");
// },
// error: function () {
// console.log('there is some error');
// }
data: { c: request },
dataType: 'json',
success: function (data) {
result.html( '#category: ' + data.CategoryName + '' + '#product' + data.ProductName)
}
});
});
//});
</script>
}
Edit: Actually, as I look at your code more, and your description of the problem, I think your problem is even earlier. Your click even on the button isn't firing, because the ID should be Button1 and not Button, but the form is still submitting because your button is input type=submit, and you created the form with Html.BeginForm(). This causes the form to be submitted to the default action (the same as the get, but as a post instead... that's why it still hits the correct action) but the submitted variables are just the values of the inputs in the form. To get this working the way you intended, you need to alter the click event for the button (first so that the IDs match, or it will never fire), and then right at the beginning, prevent the normal form submission. So it should look like this:
$('#Button1').click(function(event) {
event.preventDefault();
// Do the rest of the click event handler here
});
End Edit
You're already getting the correct value with your jquery:
$('#category option:selected').text()
Would get the selected text for the #category dropdown, rather than the value. Your problem is that you're building the jquery object as a string and passing it back as the single parameter in your return. Instead, try this:
var postData = {
c: $('#category option:selected').text(),
p: $('#product option:selected').text()
};
then, in your ajax request:
data: postData
Also, for what it's worth, there's a much simpler shorthand for this type of ajax request:
$.post('/Car/Index', postData).done(function(data) {
//Do something with the server response here
result.html( '#category: ' + data.CategoryName + '' + '#product' + data.ProductName)
});
Note that success, error, and complete callbacks have been deprecated, and replaced by done(), fail(), and always() respectively.
Edit to address comment:
I'll answer your last question first... Yes!! in a browser (most modern browsers have some version of this) press the f12 key. This should open a developer tools window of some sort. Look for a tab with javascript stuff in it... in firefox, the tab is called debugger, in chrome it's called sources. I think it's called debugger in IE too, but I don't remember. Anyway, on this tab, find the page with your javascript code (if you do it all inline, which is what your example has, it will be the page named the same as your controller action, without an extension). this should bring up the 'view source' version of the page. You can set breakpoints in the javascript here, and when the browser gets to them it will break, and you can step through, over, or into just like you would in server side debugging in vs. You can also set a watch, and monitor the value of variables.
To send the data to the browser in your post, you want to send a JSON object which contains the data. JSON is just a series of key/value pairs, really. So a JSON object looks like this:
{ "key":"value" }
That is a very simple JSON object. If I were to store that in a variable (for example postData), like this:
var postData = { "key":"value" };
I could access the value for "key" by saying postData.key, and that would get me "value". In order to send the data to your controller action, you need to construct a JSON object which has properties that match the parameters that your controller action needs. In your example, you have two properties on your POST controller action: (string c, string p), So the JSON object you pass in needs to have a property (or KEY) named c, and one named p. Keep in mind that both javascript AND c# are case-sensitive, so your properties in your JSON object need to match exactly the names of the parameters your controller action wants.
So your controller action needs a JSON object which looks like this:
{ "c":"some value", "p":"some other value" }
Notice we separate the properties with a comma.. you can put any number in here. If your controller action has optional parameters, you can leave them out, but if not, they all need to be specified here, or you'll get a 404 error. So that object above is exactly what your controller wants to see, but the values aren't right obviously, we need to grab those from your select lists. So to do that, we use the jquery you already had, to get the text from the selected options, and we're left with this:
var postData = {
c: $('#category option:selected').text(),
p: $('#product option:selected').text()
};
Note that when specifying the name of the key, if it doesn't have spaces or anything weird in it, we can omit the quotes.
So now we have a properly formatted JSON object which matches what your controller action wants to receive. We just need to send this with an ajax post. Using the type of post you originally had, would look like this:
$.ajax({
type: 'POST',
//contentType: "application/json;charset=utf-8", This line is not necessary, the browser will figure it out
url: '/Car/Index',
data: postData,
//dataType: 'json', (This line is not necessary, the browser will figure it out
success: function (data) {
result.html( '#category: ' + data.CategoryName + '' + '#product' + data.ProductName)
}
});
Note that there are a couple of lines commented out. They are not necessary for this type of call, the browser is pretty good at figuring out what's needed.
Alternatively, I prefer a shorthand way of specifying posts like this, which I mentioned above, but i'll put it here again:
$.post('/Car/Index', postData).done(function(data) {
result.html( '#category: ' + data.CategoryName + '' + '#product' + data.ProductName)
});
This does exactly the same thing is the $.ajax version above, but it's much simpler, in my opinion.
I hope that clears it up! I'm happy to answer any more questions, or provide any additional clarification.
is there a javascript where I can populate a gridview from the database? For example is there a javascript for this code?
gvRFPCorpCode is the name of my gridview
private void fillCorpCode()
{
DataSet ds = new DataSet();
data.gRowId = this.txtROWID.Text;
ds = data.GetCorpCode();
if (ds != null)
{
if (ds.Tables[0].Rows.Count > 0)
{
this.gvRFPCorpCode.DataSource = ds.Tables[0];
this.gvRFPCorpCode.DataBind();
}
}
}
ds = data.GetCorpCode(); is equal to this:
public DataSet GetCorpCode()
{
DataSet ds = new DataSet();
SqlParameter[] sqlParam = new SqlParameter[]
{
new SqlParameter("#RowId",sRowId)
};
if (this.InitDataLayerObject() == true)
{
ds = this.ExecuteQuery("dbo.sp_ESS_RFP_GetCorpCode", sqlParam);
}
return ds;
}
It's a stored procedure, here is my stored procedure "dbo.sp_ESS_RFP_GetCorpCode"
ALTER PROCEDURE [dbo].[sp_ESS_RFP_GetCorpCode]
-- Add the parameters for the stored procedure here
#RowId varchar(max)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #xml as xml
select
#xml =convert(xml,CORPCODE)
from Tbl_ESS_Request_For_Payment_Save
where ROWID=#RowId
select
tbl.col.value('CorpCode[1]', 'varchar(100)') as CorpCode,
tbl.col.value('Amount[1]', 'varchar(100)')as Amount
from #xml.nodes('/NewDataSet/Table1') tbl(col)
END
What I want is to have a javascript equivalent to private void fillCorpCode to populate it on my gridview, BTW, You might ask why I need a javascript if i already have a code in c#, It's because of some process in my program and it is difficult to explain. So please help me on this one, thank you in advance!
do like this
function getJSONData(selVal, callbackName) {
selVal = encodeURI(selVal);
var formdata = { };// any data you want to pass as input
$.ajax({
type: "POST",
url: "aspx",
data: formdata,
cache: false,
success: function (data) {
callbackName(data);
},
error: function (xhr, ajaxOptions, thrownError) {
alert('Error in processing data !!' + thrownError);
},
async: false
});
}
function createTable() {
mytable = $('<table Class="table table-striped table-bordered table-hover"></table>').attr({ id: "basicTable" });
// alert(rowData);
var rowobj = JSON.parse(rowData);
// To populate header
var firstrow;
var firstrow = $('<tr></tr>').appendTo(mytable);
$('<td></td>').text('Entity Name').appendTo(firstrow);
$('<td></td>').text('Attribute Name').appendTo(firstrow);
$('<td></td>').text(' Value').appendTo(firstrow);
// To populate rows
$.each(rowobj, function (i, obj) {
$('<td valign="middle"></td>').text(obj.ParentName).appendTo(row);
$('<td valign="middle"></td>').text(obj.ParentName1).appendTo(row);
$('<td valign="middle"></td>').text(obj.ParentName2).appendTo(row);});
}
//C# method
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true)]
public static string Mymethod(string entityName, string entityType, string filterValues)
{
List<MasterFields> fields = null;
ServiceWrapper<ILOSService>.Perform(svcClient =>
{
fields = getfields();
});
JavaScriptSerializer jss = new JavaScriptSerializer();
string output = jss.Serialize(fields);
return output;
}
I have a field for a ZIP Code.
I want that, when the person fills this field with a zip code and click in another field, triggers a event (onBlur).
This Event will execute a select in database and get the address and fill the other fields with this information.
I read that is not a good idea execute a Controller Method from the View.
So, how can I develop this?
My zip code field:
<div class="editor-field">
#Html.Label("ZIP CODE")
#Html.Editor("zipCodeClient")
</div>
Thanks!
If you have access to jQuery I would use it's ajax function to call a wcf web service that returns the relevant address information in a JSON format. Otherwise, you could create your own XHR request and parse the response.
$('#zipCodeClient').blur(function() {
var zipCode = $(this).val();
if(zipCode.length >= 5 && zipCode.length <= 10) {
$.ajax({
type: 'GET',
data: { ZipCode: zipCode },
url: 'something/ZipCodeToAddressService',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function(data) {
var responseObject = jQuery.parseJSON(data);
$('#cityTextBox').val(responseObject.City);
$('#stateTextBox').val(responseObject.State);
}
});
}
else {
// zip code not valid
}
});
In WCF:
[ServiceContract()]
public interface IAddressServices
{
[OperationContract()]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
string ZipCodeToAddressService(string ZipCode);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class AddressServices : IAddressServices
{
public string ZipCodeToAddressService(string ZipCode)
{
using (SqlConnection sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["DB"].ConnectionString))
{
using (SqlCommand sqlCmd = new SqlCommand("ZipCodeToAddressStoredProc", sqlConnection))
{
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.Parameters.Add("#Zip", SqlDbType.NVarChar).Value = ZipCode;
sqlConnection.Open();
SqlDataReader sDR = sqlCmd.ExecuteReader(CommandBehavior.CloseConnection);
DataTable tbl = new DataTable();
tbl.Load(sDR);
sDR.Close();
var citystateData = from DataRow Row in tbl.AsEnumerable()
select new
{
City = Row.Field<string>("City"),
State = Row.Field<string>("State")
};
JavaScriptSerializer js = new JavaScriptSerializer();
StringBuilder sb = new StringBuilder();
js.Serialize(cityStateData, sb);
string rtrnCityStateData = sb.ToString();
return rtrnCityStateData;
}
}
}
}