I would really appreciate a tip here... I've been looking for a solution for 4 hours now...
I have a function like so:
public virtual JsonResult LoadPreviousProductsJson(SearchResultModel rmodel, SearchCriteriaModel cmodel)
I'm trying to send data to this controller like so:
var jsonData = $('#frmSearchResult').serialize();
var stringToPost = JSON.stringify(jsonData);
var jsonData2 = $('#frmSearchProducts').serialize();
var stringToPost2 = JSON.stringify(jsonData2);
$.post('#Url.Action(MVC.Product.LoadPreviousProductsJson())', { rmodel: stringToPost, cmodel: stringToPost2 })
.done(function(data) {....
This results that the objects are Null in the controller...
If I only send 1 Json objectI am succesfull:
$.post('#Url.Action(MVC.Product.LoadPreviousProductsJson())', stringToPost)
.done(function(data) {....
but when I try to send them together, it Always fails...
Only somewhat successful thing I can do is sending the 2 objects as string and read them with Newtonsoft, but here I can't convert the strings to the corresponding objects....
model = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchResultModel>(rmodel);
model2 = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchCriteriaModel>(cmodel);
The above code just fails...
Create a new model to store your payload that is specific to your action
public class SearchViewModel {
public SearchResultModel rmodel { get; set; }
public SearchCriteriaModel cmodel { get; set; }
}
Update action to accept expected payload
public virtual JsonResult LoadPreviousProductsJson(SearchViewModel model) {
SearchResultModel rmodel = model.rmodel;
SearchCriteriaModel cmodel = model.cmodel;
//... other code
}
create the same mode on client and send one payload.
var jsonData = {};
$('#frmSearchResult').serializeArray()
.map(function(x){jsonData[x.name] = x.value;});
var jsonData2 = {};
$('#frmSearchProducts').serializeArray()
.map(function(x){jsonData2[x.name] = x.value;});
var model = { rmodel:jsonData, cmodel:jsonData2 };
var payload = JSON.stringify(model);
$.post('#Url.Action(MVC.Product.LoadPreviousProductsJson())', payload)
.done(function(data) {....}
public class SearchViewModel
{
public SearchResultModel SearchResultModel{ get; set; }
public SearchCriteriaModel SearchCriteriaModel{ get; set; }
}
public virtual JsonResult LoadPreviousProductsJson(string model)
{
var modelClass = JsonConvert.DeserializeObject<SearchViewModel>(model);
var searchResultModel = modelClass.SearchCriteriaModel;
var searchCriteriaModel = modelClass.SearchResultModel;
//... other code
}
var jsonData = $('#frmSearchResult').serialize();
var jsonData2 = $('#frmSearchProducts').serialize();
var model = { SearchResultModel: jsonData, SearchCriteriaModel:jsonData2 };
var url = "/Area/Controller/Action" + "?model=" + JSON.stringify(model) + "";
$.ajax({
url: url,
dataType: "JSON",
type: "GET",
success: function () {
}
});
Hope this might be helpful
First of all, thanks to inkosi and krish for giving tips (thumbs up for both). His answer wasn't exactly what I needed since I still was getting null values.
Here is what finally worked for me.
controller:
public virtual JsonResult LoadPreviousProductsJson(string rmodel, string cmodel){
SearchResultModel model = new SearchResultModel();
SearchCriteriaModel modelSearchCriteria = new SearchCriteriaModel();
model = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchResultModel>(rmodel);
modelSearchCriteria = Newtonsoft.Json.JsonConvert.DeserializeObject<SearchCriteriaModel>(cmodel);
.......
}
Javascript:
$.fn.serializeObject = function () {
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
var jsonDataNewResult = $('#frmSearchResult').serializeObject();
var stringjsonDataNewResult = JSON.stringify(jsonDataNewResult);
var jsonDataNewCriteria = $('#frmSearchProducts').serializeObject();
var stringjsonDataNewCriteria = JSON.stringify(jsonDataNewCriteria);
$.post('#Url.Action(MVC.Product.LoadPreviousProductsJson())',
{ rmodel: stringjsonDataNewResult, cmodel: stringjsonDataNewCriteria })
.done(function(data) {
What a day :-( , I must have tried a hundred things... I don't even remember all the things I tried..... happy to get here, now to explain to my boss why something this trivial has taken me so long.... I miss desktop programming !
PS. Thanks to these 2 SO posts:
Convert form data to JavaScript object with jQuery
Consume jQuery.serializeArray in ASP.NET MVC
Related
my deserialize Dictionary's key results in "brand[0]" when I send in "brand" to the api.
I have a class like this:
public class SearchRequest
{
public bool Html { get; set; } = false;
public Dictionary<string, HashSet<string>> Tags { get; set; }
}
// MVC Controller
[HttpPost]
public ActionResult Index(SearchRequest searchRequest)
{
...
}
And a json request like this that I post to the controller:
{
"html": true,
"tags": {
"brand": [
"bareminerals"
]
}
}
The binding seams to work and the searchRequest object is created but the resulting dictionary dose not have the key "brand" in it but insted the key "brand[0]" how can I preserve the real values I send in?
Edit: I need tags to be able to contain multiple tags, with multiple options, this was a simpel example.
One soulution to my problem is to create a custom model bind, so this is what am using now, but I dont understand why I need to, and I feel like there should be a easyer way? But am gonna leve It here anyhow.
public class FromJsonBodyAttribute : CustomModelBinderAttribute
{
public override IModelBinder GetBinder()
{
return new JsonModelBinder();
}
private class JsonModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stream = controllerContext.HttpContext.Request.InputStream;
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
var checkoutOrderDataStr = reader.ReadToEnd();
return JsonConvert.DeserializeObject(checkoutOrderDataStr, bindingContext.ModelType);
}
}
}
}
I'm not sure what is going on with your setup. You should not need a custom binder. I still think the problem is most likely with your calling code - whatever you're using as a client.
I'm using Asp.net Core 3.1. Here's what I threw together as a quick test.
Created Asp.net Core web application template with MVC. I declared two classes - a request POCO and a result POCO. The request was your class:
public class SearchRequest
{
public bool Html { get; set; } = false;
public Dictionary<string, HashSet<string>> Tags { get; set; }
}
The result was the same thing with a datetime field added just for the heck of it:
public class SearchResult : SearchRequest
{
public SearchResult(SearchRequest r)
{
this.Html = r.Html;
this.Tags = r.Tags;
}
public DateTime RequestedAt { get; set; } = DateTime.Now;
}
I Added a simple post method on the default HomeController.
[HttpPost]
public IActionResult Index([FromBody] SearchRequest searchRequest)
{
return new ObjectResult(new SearchResult(searchRequest));
}
I added a console Application to the solution to act as a client. I copied the two class definitions into that project.
I added this as the main method. Note you can either have the camel casing options on the request or not - asp.net accepted either.
static async Task Main(string[] _)
{
var tags = new[] { new { k = "brand", tags = new string[] { "bareminerals" } } }
.ToDictionary(x => x.k, v => new HashSet<string>(v.tags));
var request = new SearchRequest() { Html = true, Tags = tags };
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
var json = JsonSerializer.Serialize(request, options);
Console.WriteLine(json);
using (var client = new HttpClient())
{
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("http://localhost:59276", content);
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<SearchResult>(data, options);
Console.WriteLine(data);
var keysSame = Enumerable.SequenceEqual(request.Tags.Keys, result.Tags.Keys);
var valuesSame = Enumerable.SequenceEqual(request.Tags.Values.SelectMany(x => x),
result.Tags.Values.SelectMany(x=>x));
Console.WriteLine($"Keys: {keysSame} Values: {valuesSame}");
}
}
This outputs:
{"html":true,"tags":{"brand":["bareminerals"]}}
{"requestedAt":"2020-10-30T19:22:17.8525982-04:00","html":true,"tags":{"brand":["bareminerals"]}}
Keys: True Values: True
I have an ASP.NET MVC Controller defined like so:
[HttpPost]
public string uploadQAImage(FileUploadClass fileUploadClass, HttpPostedFile image)
{
}
And this what the class FileUploadClass looks like.
public class FileUploadClass
{
public string job { get; set; }
public string createdBy { get; set; }
public string itemId { get; set; }
}
What I am trying to do with Alamofire in iOS is call this Controller, I have tried using parameters:
func saveQAPhotos(_ cellHolder: PhotoClass, completion: #escaping (_ result: Dictionary<String, Any>) -> Void)
{
let parameters: Parameters = [
"job" : cellHolder.job!,
"itemId" : cellHolder.itemId!,
"createdBy" : appDelegate.username!
]
let urlComponents = NSURLComponents(string: webservice + "uploadQAImage");
urlComponents?.user = appDelegate.username;
urlComponents?.password = appDelegate.password;
let url = urlComponents?.url;
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
if let data = cellHolder.photo {
multipartFormData.append(data, withName: "image", fileName: "image.png", mimeType: "image/png")
}
}, usingThreshold: UInt64.init(), to: url!, method: .post, headers: nil) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
if let result = response.result.value {
let jsonData = result as! Dictionary<String, Any>
completion(jsonData)
}
}
case .failure(_):
print("error")
}
}
}
But that didn't work on the ASP.NET side I get this error:
can't bind multiple parameters to the request's content
I have also tried sending the data as [AnyHashable: Any] like so:
func saveQAPhotos(_ cellHolder: PhotoClass, completion: #escaping (_ result: Dictionary<String, Any>) -> Void)
{
var jsonDict = [AnyHashable: Any]()
jsonDict["job"] = cellHolder.job
jsonDict["itemId"] = cellHolder.itemId
jsonDict["createdBy"] = appDelegate.username
let jsonData: Data? = try? JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted)
let urlComponents = NSURLComponents(string: webservice + "uploadQAImage");
urlComponents?.user = appDelegate.username;
urlComponents?.password = appDelegate.password;
let url = urlComponents?.url;
Alamofire.upload(multipartFormData: { (multipartFormData) in
multipartFormData.append(jsonData!, withName: "fileUploadClass", mimeType: "application/json")
if let data = cellHolder.photo {
multipartFormData.append(data, withName: "image", fileName: "image.png", mimeType: "image/png")
}
}, usingThreshold: UInt64.init(), to: url!, method: .post, headers: nil) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
if let result = response.result.value {
let jsonData = result as! Dictionary<String, Any>
completion(jsonData)
}
}
case .failure(_):
print("error")
}
}
}
Same error as before
can't bind multiple parameters to the request's content
Now when I change the ASP.NET Controller to only get FileUploadClass like so:
[HttpPost]
public string uploadQAImage(FileUploadClass fileUploadClass)
{
}
I get this error:
Object reference not set to an instance of an object.
I can only assume if I do fix the can't bind multiple parameters to the request's content error I will get this error next.
I am pretty sure I am sending the data incorrectly, so I guess my question is how do I send data from Alamofire upload method to an ASP.NET MVC method?
This is my method..
public ActionResult AddModelAliasData(ModalAliasModel modalAliasModel)
{
if (!ModelState.IsValid)
{
ModelState.LogModelStateError();
throw new BusinessException("COMMON_ERROR");
}
var response = _vehicleDataBusinessService.AddModelAliasData(modalAliasModel);
return Json(response);
}
As i am new to unit testing ... I am confused for some things, for the given above method in controller we are getting the json response in return. I want to check whether the return Json we are getting back has success true or false How should i do it???
The Response object result is
public class GetVehicleDataAliases : DefaultResponse
{
public List<SearchData> FindData { get; set; }
public List<VehicleDto> MakeDtos { get; set; }
}
The defaultResponse are Success , ErrorCode, ErrorMessage
I had wrote the unit test for this
[Test]
public void ShouldReturnJsonInAddMakeAlias()
{
var data = new GetVehicleDataAliases
{
MakeDtos = new List<VehicleDto>(),
Success = true,
FindData = new List<SearchData>()
};
mockVehicleDataBusinessService.Setup(x => x.AddMakeAliasData(It.IsAny<MakeAliasModel>())).Returns(() => data);
var vehicleDataController = new VehicleDataController(mockVehicleDataBusinessService.Object);
var result = vehicleDataController.AddMakeAliasData(makeAliasModel) as JsonResult;
Assert.AreEqual(data, result.Data);
}
But I am Able to clarify that is it the Right way to check for True or False Json result
I have two problems. 1 is that the c# function does not get the js values. Even though when I step through the javascript, the values are in fact there.
The other problem is that my c# post returns a 404 not found error and I see that the debugger never even goes to my other service on localhost123.
Any advice?
This is my angularjs code below
var AddToGroupIds = [];
var RemoveFromGroupIds = [];
angular.forEach($scope.vm.mailingLists, function(f){
if(f.Selected == true){
AddToGroupIds.push(f.Id);
}
else{
RemoveFromGroupIds.push(f.Id);
}
});
return $http({
method: 'POST',
url: '/Contacts/UpdateSubscription',
data: { AddToGroups: AddToGroupIds, RemoveFromGroups: RemoveFromGroupIds, email: 'test#test.com' }
})
.then(function (data) { return data.data; })
this is my c# code below:
[HttpPost]
public void UpdateSubscription(List<int> AddToGroups, List<int> RemoveFromGroups, string email)
{
HttpWebRequest req = null;
var text = "";
try
{
req = HttpWebRequest.CreateHttp(string.Format("http://localhost:123/api/Api/ImmediateUpload?AddToGroupIds={0}&RemoveFromGroupIds={1}&email={2}", AddToGroups, RemoveFromGroups, email));
req.PreAuthenticate = true;
req.Credentials = new NetworkCredential("123", "123", "123");
var res = req.GetResponse();
using (var sr = new StreamReader(res.GetResponseStream()))
{
text = sr.ReadToEnd();
}
res.Close();
}
catch (Exception e)
{
text = e.ToString();
}
}
You cannot post multiple parameters that way. See this article. You can use JObject as the article mentions, or create a UpdateSubscriptionRequest model containing the parameters:
public class UpdateSubscriptionRequest
{
public List<int> AddToGroups { get; set; }
public List<int> RemoveFromGroups { get; set;}
public string Email { get; set;}
}
And change your controller action to:
public void UpdateSubscription([FromBody]UpdateSubscriptionRequest request)
I have the following Action in my layouts Controller
public JsonResult getlayouts(int lid)
{
List<layouts> L = new List<layouts>();
L = db.LAYOUTS.Where(d => d.seating_plane_id == lid).ToList()
return new JsonResult { Data = L, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
I am calling this Action from another controller like so:
layoutsController L = new layoutsController();
JsonResult result = L.getlayouts(lid);
My question is: how can I get the data from result object?
Well, have a look how you're building the object:
new JsonResult { Data = L, JsonRequestBehavior = JsonRequestBehavior.AllowGet }
You're setting the L variable to a property called Data. So just read that property:
List<layouts> L = (List<layouts>)result.Data;
There's nothing special about the fact that it's an MVC controller action.
You're simply calling a method which returns an object that was constructed in the method, and reading properties from that object. Just like any other C# code.
I have my class:
public class ResponseJson
{
public string message { get; set; }
public bool success { get; set; }
}
in my method SendEmail
private async Task<JsonResult> SendEmailAsync(ApplicationUser user, string returnUrl, string empleadoNombre, string pwdInic)
i will return my JsonResult
ResponseJson response = new ResponseJson();
response.success = true;
response.message = "OperaciĆ³n exitosa";
return new JsonResult( response);
to read the result returned from my SendEmail method
JsonResult emailSend = await SendEmailAsync(user, returnUrl, empleadoNombre, pwdInic);
ResponseJson response = new ResponseJson();
try
{
string json = JsonConvert.SerializeObject(emailSend.Value);
response = JsonConvert.DeserializeObject<ResponseJson>(json);
}
catch(Exception e)
{
}