json data not received by asp.net mvc action method - c#

I am trying to get some data to a controller from client side script, I am stringfying my data so I receive something like:
{"Name":"","Description":"","FieldType":"radio","Fields":[{"Field":{"Name":"something","Value":"nameit"}},{"Field":{"Name":"something else","Value":"dontnameit"}}]}
I will need to validate my data on the controller however, in my action I am recieving a null for some reason, if I use object or string? Why is that?
I have had a look into a lot of other posts but it is not clear, do I need to create my own custom IValueProvider implementation? I think there is one available in the ms futures assembley, I tried to locate the file as I do not want all the code inside the dll, but I could not find it...
Any pointers would be appreciated...
Controller:
[HttpPost]
public JsonResult AddField(string field) //or object
{
//code here
}
Edit: I have followed the post by phill haack but had some errors actually returning the strongly typed object to my view...
my ajax call..
{
url: url,
type: "post",
dataType: 'json',
traditional: true,
data: jsondata, // { "field" : jsondata},
contentType: 'application/json; charset=utf-8',
...
}
I created a custom value provider...
public class Jsonify : ValueProviderFactory
{
public Jsonify() { }
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
var jsonData = GetDeserializedJson(controllerContext);
if (jsonData == null)
{
return null;
}
//currently used by mvc2 futures
//return new DictionaryValueProvider<object>(backingStore,
//CultureInfo.CurrentCulture);
// what do I return?
}
private static object GetDeserializedJson(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
// not JSON request
return null;
}
StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
string bodyText = reader.ReadToEnd();
if (String.IsNullOrEmpty(bodyText))
{
// no JSON data
return null;
}
//json.net
var jsonData = JsonConvert.DeserializeObject<SurveyField>(bodyText);
return jsonData;
}
}
Controller:
public JsonResult AddSimpleField(SurveyField field) { ... }

You may take a look at the following blog post which illustrates how you could use a custom JsonValueProviderFactory to send a JSON encoded string from client scripts to a controller action and have this action receive it as a strongly typed model and benefit from the validation of the default model binder:
[HttpPost]
public ActionResult AddField(SomeViewModel model)
{
if (!ModelState.IsValid)
{
// the client sent an invalid data
return Json(new { IsSuccess = false });
}
// the model passed validation => do some processing with this model
return Json(new { IsSuccess = true });
}
As Phil Haack explains it this custom JsonValueProviderFactory is only necessary if you are working with ASP.NET MVC 2 and is built-in ASP.NET MVC 3 so it should work out of the box.

Related

Call Method in Controller from View(cshtml)

Hi im trying to call a method from controller from view just to display the "staff_name"
Controller
public JsonResult getStaffName(int staff_id)
{
var staffname = (from a in db.master_staff where b.staff_id == staff_id
select a.staff_name).SingleOrDefault();
return Json(staffname,JsonRequestBehavior.AllowGet);
}
View
int[] staff_id = { 24,25,26 };
#foreach (var n in staff_id){
//call method getStaffName from Controller to get "staff_name"
}
it suppose to get the "staff_name" according to the "staff_id"
is there any possible method for this situation?
To call method from controller to view you have to use ajax call from view.
here is ajax call syntax in asp.net mvc:
$.ajax({
type: "GET",
url: '#Url.Action("controller method name", "controller name")',
data: { searchText: value },
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (result) {
},
error: {
}
});
type can be GET or POST depending on your controller method type.
In URL attribute two parameters are passed the first one is controller method name and second one is controller name.
In data attribute you have to pass the values which are required to pass in controller from view such as parameters in controller method.
In success and error attribute you have to write a block of code which should be executed in both cases. such as display data on UI upon success and error message on failure.
Do not do this. It is the way to the Dark side :) And each JSON request is taking some time.
Create model to store your staff
Fill it in controller (or even better in some business logic class)
Display in your view
public ActionResult Staff()
{
// This would be nice to have as separate Data Access Layery class
var staffs = GetStaffs();
return View(staffs);
}
private static StaffDto[] GetStaffs()
{
// One call to database is better that several
var staffs = db.master_staff
.Where(x => x.staff_id > 0) // Any other condition, if needed
.Select(x => new StaffDto(x.staff_id, x.staff_name))
.ToArray();
return staffs;
}
// Data Transfer Object class to separate database from presentation view
public class StaffDto
{
public int StaffId { get; set; }
public string Name { get; set; }
public StaffDto(int staffId, string name)
{
StaffId = staffId;
Name = name;
}
}
Your view file (Staff.cshtml in my case)
#model IEnumerable<TestMvc.Controllers.StaffDto>
<div>
#foreach (var staff in Model)
{
#staff.Name<br />
}
</div>
And, as bonus, you could unit test it easily
private readonly HomeController _homeController = new HomeController();
[TestMethod]
public void Staff_CheckCount()
{
var result = _homeController.Staff();
Assert.IsInstanceOfType(result, typeof(ViewResult));
var actual = ((ViewResult)result).Model as StaffDto[];
Assert.IsNotNull(actual);
Assert.AreEqual(3, actual.Length);
}
You can do it like this:
#Html.Action("getStaffName", "YourController", staff_id)
For more info, look at child actions: https://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx/
However, I do not know what you are trying to achieve with this.

Post int directly to web api without model class

I am new to C#. I tried to create a post service with using int. All get and post service are working fine.
But when I pass parameter to post service, it's always null. But after creating a class it works fine. Can we pass direct int to service or we must have to create a model class for it?
[System.Web.Http.HttpPost]
public IHttpActionResult GetUserByID(int id)
{
var user = userList.FirstOrDefault((p) => p.Id == id);
if (user== null)
{
return NotFound();
}
return Ok(user);
}
but it always send 0 . but when i create a class and add that int as attribute it works fine.
Working code
[System.Web.Http.HttpPost]
public IHttpActionResult GetUserByID(data id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Ok();
}
public class data
{
[Required]
public int id { get; set; }
}
Edit
are my header accurate?
I think you need to add [FromBody] to the parameter:
[System.Web.Http.HttpPost]
public IHttpActionResult GetUserByID([FromBody]int id)
{
var user = userList.FirstOrDefault((p) => p.Id == id);
if (user== null)
{
return NotFound();
}
return Ok(user);
}
According to the docs: Parameter Binding in ASP.NET Web API
By default, Web API uses the following rules to bind parameters:
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from
a string.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
It goes on to say: Using [FromBody]
To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter
UPDATES - to get [HttpPost] working...
As #Shahbaz suggested below, make sure that you've got the Content-Type header set to application/json, otherwise you will get error message saying:
The request entity's media type 'text/plain' is not supported for this resource.
Also, make sure you're posting just the id in the Request Body e.g. 1, as opposed to posting the id wrapped in a JSON object as a key/value pair { "id": "1" }.
FINALLY - consider using [HttpGet] instead...
It's worth pointing out, because you are now just sending a single int to get a single record, even if you can get this working using [HttpPost], it's still probably best to change it to [HttpGet] which is semantically correct - you are getting a user record, and don't actually need to post anything at all. So something like this might be better:
[System.Web.Http.HttpGet]
[Route("api/users/{id}")]
public IHttpActionResult GetUserByID(int id)
{
var user = userList.FirstOrDefault((p) => p.Id == id);
if (user== null)
{
return NotFound();
}
return Ok(user);
}
Then put your id in the request URL, something like:
https://yourdomain/api/users/1
The above example makes use of Attribute Routing which can help you create your own custom URLs to target your own API Action Methods.
Send Data to Web Api by a Jquery Like Below :
function PostSth(fid){
$.ajax({
url: apiBaseUrl + 'api/Controller/ActionMethod',
type: 'Post',
data:`'`+fid+`'`,
contentType: "application/json; charset=utf-8",
success: function (data) {
alert(data);
},
error: function () {
alert('Error');
}
});
}
Don't Forget
data:`'`+fid+`'`,
above.
and do in the Code Behind Part :
public string ActionMethod([FromBody]int fid)
{
string result = string.Empty;
//TODO: Your Code
return result;
}

ASP.net API get call returns controller class, not data

I've created an API in ASP.net. My Sencha SPA is attempting to get data from it. Unfortunately the get call is returning the controller class rather than the data I want. Any ideas?
Here is the get method:
private firstAPIContext db = new firstAPIContext();
// GET: api/AccountModels
public IQueryable<Account> GetAccountModels()
{
return db.Accounts;
}
// GET: api/AccountModels/5
[ResponseType(typeof(Account))]
public async Task<IHttpActionResult> GetAccountModel(string username)
{
Account accountModel = await db.Accounts.FindAsync(username);
if (accountModel == null)
{
return NotFound();
}
return Ok(accountModel);
}
Here is the rest proxy code from Sencha:
Ext.define('MyApp.store.AccountsStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.AccountModel',
'Ext.data.proxy.Rest'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
storeId: 'AccountsStore',
model: 'MyApp.model.AccountModel',
proxy: {
type: 'rest',
url: '/Controllers/AccountsController.cs'
}
}, cfg)]);
}
});
Please make sure that you have added a JSON Reader on your Sencha Store.So that you can read the response send by the server.Also make sure that you are sending JSON data back from the server for this add the following lines in you Global.axpx
var config=GlobalConfiguration.Configuration;
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
And also change the url to like this
url: '/Controllers/AccountsController/'
This seems to be the main reason for me because you have added an extension
.cs thats why when u call the method it returns the whole controller class instead of the data.Try to remove this extension too and do all what i have suggest.Hope it will works fine

ASP.NET MVC JSON body with POST

I am new to ASP.NET MVC and learning. So far I have figured out how I can create a JSON Object and return that as a response to a request. However, I'm not able to pass a JSON body as part of a POST request like I normally did using Java.
Here is the code how I did this there -
#Path("/storeMovement")
#POST
#Consumes("application/json")
#Produces("application/json")
public String storeTrace(String json) {
JSONObject response = new JSONObject();
JSONParser parser = new JSONParser();
String ret = "";
try {
Object obj = parser.parse(json);
JSONObject jsonObj = (JSONObject) obj;
RecordMovement re = new RecordMovement((double) jsonObj.get("longitude"), (double) jsonObj.get("latitude"), (long) jsonObj.get("IMSI"));
ret = re.Store();
// Clear object
re = null;
System.gc();
response.put("status", ret);
} catch (Exception e) {
response.put("status", "fail " + e.toString());
}
return response.toJSONString();
}
I tried the same in the ASP.NET Action method but the value in the string parameter a is null as seen while debugging. Here's the code for the Action method -
public string Search(string a)
{
JObject x = new JObject();
x.Add("Name", a);
return x.ToString();
}
It works fine when I use an Object (for example - Book) like so -
public string Search(Book a)
{
JObject x = new JObject();
x.Add("Name", a.Name);
return x.ToString();
}
In that case, the book's name gets de-serialized just fine as I would expect. The class definition for the Book class -
public class Book
{
public int ID { get; set; }
public string Name { get; set; }
}
Can somebody please advise what I'm doing wrong? Is there no way to take in a string and then de-serialize? I'd like to be able to take in JSON without having to use an Object
As for as understand you want pass entire of request body to a string without any binding so you could handle passed string data with your desired way.
To aim this purpose simply write your own model binder:
public class RawBodyBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if(typeof(string)!=bindingContext.ModelType)
return null;
using (var s = new StreamReader(controllerContext.HttpContext.Request.InputStream))
{
s.BaseStream.Position = 0;
return s.ReadToEnd();
}
}
}
And in you action method assign your binder to desired parameter:
public string MyAction([ModelBinder(typeof(RawBodyBinder))]string json)
{
}
But MVC has own JSON model binder as well if your data is a standard JSON and in request body with Content-Type: application/json header you could use MVC's JSON model binder to activate JSON model binder simply add following line in Global.asax.cs file:
protected void Application_Start()
{
// some code here
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
}
The first thing in asp.net mvc to post a data is to decorate the method with this attribute [Httpost]
it's mean passing a string
should look like
[HttpPost]
public string Search(string a){
// your code
}
The default value is [HttpGet] that get parameters from url. For Post request you need to.
Edit:
And look the answer from vinayan
with jquery:
$.ajax({
method: "POST",
url: "Home/Search",
data: {'a': 'yourstring'}
})
The name of the parameter you send is used for the de-serialization. So in in this case, "a" should be part of json.
public string Search(string a)
so you will have to use,
$.ajax({
method: "POST",
url: "Home/Search",
data: {'a': 'yourstring'}
})

Posting and receiving json with MVC Web API

I have seen similar case to mine answered but I have specific need that always differed from others problem.
I am sending json data from my html page to the MVC Web API. Unfortunately the data I am receiving is ALWAYS null (I have tried a lot of different things here). The thing is I really need to received the data as json (string), because my data is very complex and cannot be simply deserialized by the Web API (I have my own custom deserializer that does it).
Heres the code!
First, the web api controller that will receive the ajax post request
public class ActivityController : ApiController
{
// POST api/activity
public string Post([FromBody]string json)
{
}
}
Then, the ajax request itself
$.ajax("/api/activity", {
contentType: "application/json",
data: { json: ko.mapping.toJSON(fusionedModel) },
type: "POST",
success: function (result) {
...
}
});
As far as the data is concerned, it is rendered well (I've used that same request with MVC (not Web Api) and the server was receiving the string perfectly... now for some reason, in my web api controller, the "json" param is ALWAYS null. As I said before, it is IMPORTANT that I receive the data as a json string.
EDIT : I found that my question is a duplicate of this one : POST JSON with MVC 4 API Controller
But I really don't like the answer... having to create an object just to encapsulate the string is very dirty...
I recommend you avoid using standard body parameter binding as that mechanism assumes that you are trying to deserialize the body into a CLR object. Try this,
public class ActivityController : ApiController
{
// POST api/activity
public async Task<HttpResponseMessage> Post(HttpRequestMessage request)
{
var jsonString = await request.Content.ReadAsStringAsync();
return new HttpResponseMessage();
}
}
If you really want to use parameter binding, you can do this.
public HttpResponseMessage Post(JToken jToken)
{
return new HttpResponseMessage()
{
Content = new StringContent(jToken.ToString())
};
}
Please try to use the [HttpPost] Attribute that can be located on System.Web.Http;
public class ActivityController : ApiController
{
// POST api/activity
[HttpPost]
public string Post([FromBody]string json)
{
}
}
I could be wrong here, but it looks like you haven't included your action in the post URL. Try changing
$.ajax("/api/activity", {
contentType: "application/json",
data: { json: ko.mapping.toJSON(fusionedModel) },
type: "POST",
success: function (result) {
...
}
});
To
$.ajax("/api/activity/POST", {
contentType: "application/json",
data: { json: ko.mapping.toJSON(fusionedModel) },
type: "POST",
success: function (result) {
...
}
});

Categories