How can I pass multiple Json objects to ASP.net MVC controller? - c#

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

MVC - How to deserialize a Dictionary without having brackets [0] added to the key?

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

Swift 4 Alamofire upload file with parameters to ASP.NET MVC Controller

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?

Checking For Json response is true or false in unit testing with Nunit?

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

angularjs data not posting from javascript to C# and also service reference not connecting

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)

How to retrieve JsonResult data

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)
{
}

Categories