I am calling a Web API Post Action passing a JSON parameter.
My custom model is as follows:
[Serializable]
public class Model
{
public int? prop1 {get; set;}
public bool prop2 {get; set;}
}
Web API is:
public void Post(Model model)
{
if (model != null && model.prop1 ==5 )
{
// Do something
}
}
The JSON i pass from client is:
var value = {
prop1: 4,
prop2: true
};
And the AJAX call from client is:
.ajax('/api/MyController', {
type: "POST",
contentType: "application/json",
data: JSON.stringify(value),
success:function(data){
alert(Success);
}
});
However, the binding of the model properties never works in the WebAPI action. The "model" param comes back instantiated (it is not null), however all the properties inside are default values and not the values I pass from client. If I remove the [Serializable] attribute from the Model class, it works fine. I cannot remove this attribute since this object gets stored in SQL based session. What are the ways I can get this binding to work without removing the [Serializable] attribute
Remove [Serializable] from your Model and it should do it. Not sure why, but it's not working when the class is marked as Serializable.
Related
The front-end of my application can send unknown number of POST values inside a form. Fro example in some cases there will be 3 values coming from certain textboxes, in some cases there will be 6 values coming from textboxes, dropdowns etc. The backend is ASP.NET Web API. I know that a simple .NET value can be passed in URI parameter to a "POST Action" using FromURI attribute and a complex type can be passed in body and fetched using FromBody attribute, in any POST Action. But in my case the number of form data values will NOT be constant rather variable and I can't use a pre-defined class to hold values using 'FromBody' attribute.
How can I tackle this situation?
You can use the FormDataCollection from the System.Net.Http.Formatting namespace.
public class ApiFormsController : ApiController
{
[HttpPost]
public IHttpActionResult PostForm(FormDataCollection form)
{
NameValueCollection items = form.ReadAsNameValueCollection();
foreach (string key in items.AllKeys)
{
string name = key;
string val = items[key];
}
return Ok();
}
}
Try to send this properties as list of properties. Make model something like this:
public class PostModel
{
public IEnumerable<PropertyModel> Properties { get; set; }
}
public class PropertyModel
{
public string Value { get; set; }
public string Source { get; set; }
// etc.
}
And action:
public IHttpActionResult Post(PostModel model)
{
//Omited
return Ok();
}
I have a WebApi Controller method that accepts a derived class:
//Controller Method
public IHttpActionResult Test(DerivedClass m)
{
return Ok(true);
}
//base class
public class BaseClass
{
public int Id{ get; set; }
}
//derived class
public class DerivedClass : BaseClass
{
public new int? Id { get; set; }
}
I am posting with Id = 1
If I POST to this controller method with JSON data type it works fine (I get an object with Id=1), but if I send x-www-form-urlencoded data then the object is null.
Now, if I change the controller method to accept the BaseClass and use x-www-form-urlencoded data then the object is not null (Id=1). Also, if I modify the Derived class and remove the new int? property it works fine as well.
This is leading me to believe there is an issue with Json.Net not deserializing x-www-form-urlencoded data to objects that hide derived class properties. Any suggestions on how to resolve?
One thing to consider is that the nullable int property will have a different signature from non-nullable, so you may not be actually overriding the property if that was your intention.
I am using WEB API 2.
In my request I pass the request as Collection of Mytype
MyType has following properties:
Id(int),
PaymentId(int).
I need the following validation. Every Id of the request should be Unique.
Do I need to write custom validation or Is there any builtin DataAnnotationValidation exist for that?
The only way I can think of is writing a model class inheriting IValidatableObject or writing a custom validation attribute for the entire model. Web API will validate your model on the custom validation logic.
public class MyModel : IValidatableObject
{
public IEnumerable<MyType> MyTypes { get; set;}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
foreach(var myType in this.MyTypes)
{
// do validation logic
if (error)
{
yield return new ValidationResult("All Ids must be unique!")
}
}
}
}
Consider a model class
public class MyModel
{
public string Id { get; set; }
/* some other properties */
}
And a controller
public class MyController
{
[HttpPut]
public ActionResult Update(string id, MyModel model)
{
/* process */
}
}
The routing is registered as follows:
protected override void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("MyController",
"api/my/{id}",
new { action = "Update", controller = "My"},
new { httpMethod = new HttpMethodConstraint(new[] { "PUT" }) });
}
When using a REST client and sending MyModel serialized as a JSON or XML request to this controller, a null "Id" property of "MyModel", overrides the "id" parameter of the action method, even if you post it to http://api.example.com/api/my/10.
How does one force ASP.NET MVC 3 to populate the "id" property from the URL (in this case "10") and ignore the "Id" property of the "MyModel"?
Note that I'm not using ASP.NET Web API.
Try using attribute [FromUri]. It's in "System.Web.Http". This attribute on action param id indicates it should be bonded using the url request.
using System.Web.Http;//at the top
public class MyController
{
[HttpPut]
public ActionResult Update([FromUri]string id, MyModel model)
{
/* process */
}
}
For MVC3 try to include web-api package(from nuget or manually) to use [FromUri] attribute. IF that is not possible then the only way I can think of getting it is from this.HttpContext.Request.QueryString["id"]
Instead of having id as a action method paramter declare it in action body. May have to change the url query api/my?id=1212. First try using api/my/{id} format.
var id = this.HttpContext.Request.QueryString["id"];
I am creating an ASP.NET MVC3 restful web service to allow reports to be uploaded from a set of servers. When a new report is created, I want the client app to do a PUT to
http://MyApp/Servers/[ServerName]/Reports/[ReportTime]
passing the content of the report as XML in the body of the request.
My question is: how do I access the content of the report in my controller? I would imagine that it is available somewhere in the HttpContext.Request object but I am reluctant to access that from my controller as it is not possible(?) to unit test that. Is it possible to tweak the routing to allow the content to be passed as one or more parameters into the controller method? The outcome needs to be RESTful, i.e. it has to PUT or POST to a URL like the one above.
Currently my routing is:
routes.MapRoute(
"SaveReport",
"Servers/{serverName}/Reports/{reportTime",
new { controller = "Reports", action = "Put" },
new { httpMethod = new HttpMethodConstraint("PUT") });
Is there any way to modify this to pass content from the HTTP request body into the controller method?
The controller method is currently:
public class ReportsController : Controller
{
[HttpPut]
public ActionResult Put(string serverName, string reportTime)
{
// Code here to decode and save the report
}
}
The object I am trying to PUT to the URL is:
public class Report
{
public int SuccessCount { get; set; }
public int FailureOneCount { get; set; }
public int FailureTwoCount { get; set; }
// Other stuff
}
This question looks similar but doesn't have any answer.
Thanks in advance
Seems like you just need to use the standard ASP.NET MVC model binding capability with the slight wrinkle that you would doing an HTTP PUT instead of the more common HTTP POST. This article series has some good samples to see how model binding is used.
Your controller code would then look like:
public class ReportsController : Controller
{
[HttpPut]
public ActionResult Put(Report report, string serverName, string reportTime)
{
if (ModelState.IsValid)
{
// Do biz logic and return appropriate view
}
else
{
// Return invalid request handling "view"
}
}
}
EDIT: ====================>>>
Jon added this code to his comment as part of the fix so I added it to the answer for others:
Create a custom ModelBinder:
public class ReportModelBinder : IModelBinder
{
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var xs = new XmlSerializer(typeof(Report));
return (Report)xs.Deserialize(
controllerContext.HttpContext.Request.InputStream);
}
}
Modify the Global.asax.cs to register this model binder against the Report type:
ModelBinders.Binders[typeof(Report)] = new Models.ReportModelBinder();