I'm having some issues trying to make a HTTP PUT (or POST) using WebClient against a MVC 2 controller. The exception is:
The parameters dictionary contains a null entry for parameter 'total'
of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult Company(System.Guid, Int32, Int32, System.String)'开发者_如何学Go
The controller action is:
[HttpPut]
public ActionResult Company(Guid uid, int id, int total, string url)
The route is:
routes.MapRoute(
"CompanySet",
"job/active/{uid}/company/{id}",
new { controller = "Job", action = "Company" }
);
The client code is:
var putData = Encoding.ASCII.GetBytes("total=919&url=test");
var client = new WebClient();
client.UploadData("http://localhost/job/active/1/company/2", "PUT", putData);
As you may see, what I want is to send the 'uid' and 'id' parameters via url, and the 'total' and 'url' parameters as part of the PUT or POST body.
I've also tried to merge the latter parameters into a class (i.e., CompanySetMessage), doing it no longer raises an exception but I dont receive the values on the server side.
Any ideas? Thank you!
First, you need to set either a PUT or POST ActionMethodSelectorAttribute
:
[HttpPut] // <- ActionMethodSelectorAttribute
public ActionResult Company(Guid uid, int id, int total, string url)
{ // TODO: Bananas }
Example:
Solving the issue using forms in the view and separate methods for each request (preferred in ASP.NET MVC architecture):
Edit Company Method
public ActionResult Company(int id)
{
return View(companyData.Single(x => x.Id == id)); // The filter can be anything
}
POST Action Method
[HttpPost]
[ActionName("Company")] // <- Allows to create multiple Actions with the same parameters which also refer to the same ActionName
public ActionResult Company_Post(Guid uid, int id, int total, string url)
{
return Content(
String.Format("POST Values:<br />Guid: {0}<br /> Id: {1}<br /> Total: {2}<br /> Url: {3}", uid, id, total, url)
);
}
POST View Code (It's a strongly typed View for the type 'Company')
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Uid) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Uid)%>
<%: Html.ValidationMessageFor(model => model.Uid)%>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Id) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Id) %>
<%: Html.ValidationMessageFor(model => model.Id) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Total) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Total) %>
<%: Html.ValidationMessageFor(model => model.Total) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Url) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Url) %>
<%: Html.ValidationMessageFor(model => model.Url) %>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
POST Output
POST Values:
Guid: 12345678-1234-1234-1234-123456789012
Id: 1
Total: 10
Url: #1
As you can see, all values are sent back to the controller. (Without the need of collecting /initializing them first)
PUT Action Method
[HttpPut]
[ActionName("Company")] // <- Allows to create multiple Actions with the same parameters which also refer to the same ActionName
public ActionResult Company_Put(Guid uid, int id, int total, string url)
{
return Content(
String.Format("PUT: Values:<br />Guid: {0}<br /> Id: {1}<br /> Total: {2}<br /> Url: {3}", uid, id, total, url)
);
}
PUT View Code (Same View as above, just slightly changed
<% using (Html.BeginForm()) {%>
<%: Html.HttpMethodOverride(HttpVerbs.Put) %> // <- Only this one is new, it renders a different form
<%: Html.ValidationSummary(true) %>
...
PUT View Rendered Html Code
<form action="/dataentry/Company/1" method="post"><input name="X-HTTP-Method-Override" type="hidden" value="PUT" />
PUT Output
PUT: Values:
Guid: 12345678-1234-1234-1234-123456789012
Id: 1
Total: 10
Url: #1
This would be the strongly typed and safer way of sending data back and forth from controller and views. The mapping of the posted / put values is being done by the MVC framework automatically. So if you need to change something in your Domain model, you only need to add the new fields to your View and adjust the Method parameters.
Even though it isn't what you wanted, I still think it isn't that bad to have it written down somewhere :-).
Nevermind, apparently it was a problem in the client side, not the server side.
I've just changed
client.UploadData
for
client.UploadValues
using a NameValueCollection, and now it's working fine.
精彩评论