ASP.NET MVC4 Web APIアプリケーションでは、顧客を保存するためにpostメソッドを定義しています。 顧客はPOSTリクエストボディにjson形式で渡されます。 postメソッドのCustomerパラメータには、プロパティにNULL値が含まれます。
どのように修正すれば、postされたデータが顧客オブジェクトとして渡されるようになりますか?
可能であれば、Content-Type: application/x-www-form-urlencoded を使用する必要があります。
コントローラ
public class CustomersController : ApiController {
public object Post([FromBody] Customer customer)
{
return Request.CreateResponse(HttpStatusCode.OK,
new
{
customer = customer
});
}
}
}
public class Customer
{
public string company_name { get; set; }
public string contact_name { get; set; }
}
リクエスト
POST http://localhost:52216/api/customers HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
{"contact_name":"sdfsd","company_name":"ssssd"}
EDIT :31/10/2017
同じコード/アプローチは、Asp.Net Core 2.0でも動作します。大きな違いは、asp.net coreでは、Web APIコントローラとMvcコントローラの両方が単一のコントローラモデルに統合されていることです。そのため、戻り値の型は IActionResult
またはその実装 (例: OkObjectResult
) となります。
使用方法
contentType:"application/json"
送信時には JSON.stringify
メソッドを使ってJSON文字列に変換する必要がある、
そして、モデルバインダーはjsonデータをクラスオブジェクトにバインドします。
以下のコードは問題なく動作します(テスト済み)。
$(function () {
var customer = {contact_name :"Scott",company_name:"HP"};
$.ajax({
type: "POST",
data :JSON.stringify(customer),
url: "api/Customer",
contentType: "application/json"
});
});
**結果
contentType`プロパティは、JSON形式でデータを送信していることをサーバーに伝えます。JSONデータ構造を送信したので、モデルバインディングは適切に行われます。
ajaxリクエストのヘッダを調べると、Content-Type
の値がapplication/json
に設定されていることがわかります。
contentTypeを明示的に指定しなかった場合、デフォルトのコンテントタイプである application/x-www-form-urlencoded;
が使用されます。
*コメントで提起された他の可能性のある問題に対処するため、2015年11月に編集しました。
例えば、次のような複雑なビューモデルクラスをWeb apiのアクションメソッドのパラメータとして持っているとします。
public class CreateUserViewModel
{
public int Id {set;get;}
public string Name {set;get;}
public List<TagViewModel> Tags {set;get;}
}
public class TagViewModel
{
public int Id {set;get;}
public string Code {set;get;}
}
で、ウェブ API のエンドポイントが
public class ProductController : Controller
{
[HttpPost]
public CreateUserViewModel Save([FromBody] CreateUserViewModel m)
{
// I am just returning the posted model as it is.
// You may do other stuff and return different response.
// Ex : missileService.LaunchMissile(m);
return m;
}
}
*この記事を書いている時点では、ASP.NET MVC 6が最新の安定版で、MVC6ではWeb apiコントローラとMVCコントローラの両方がMicrosoft.AspNet.Mvc.Controller
ベースクラスを継承しています。
クライアントサイドからメソッドにデータを送信するには、以下のコードでうまくいくはずです。
//Build an object which matches the structure of our view model class
var model = {
Name: "Shyju",
Id: 123,
Tags: [{ Id: 12, Code: "C" }, { Id: 33, Code: "Swift" }]
};
$.ajax({
type: "POST",
data: JSON.stringify(model),
url: "../product/save",
contentType: "application/json"
}).done(function(res) {
console.log('res', res);
// Do something with the result :)
});
Web api のメソッドパラメータを [FromBody]
属性でデコレートしていない場合
[HttpPost]
public CreateUserViewModel Save(CreateUserViewModel m)
{
return m;
}
また、contentTypeプロパティの値を指定せずにモデル(JSON形式ではなく、生のjavascriptオブジェクト)を送信します。
$.ajax({
type: "POST",
data: model,
url: "../product/save"
}).done(function (res) {
console.log('res', res);
});
モデルバインディングは、モデル上のフラットなプロパティに対して機能します。この場合、Id
と Name
プロパティはパラメータ m
に適切にバインドされますが、Tags
プロパティは空のリストになります。
ショートバージョンの $.post
を使用している場合も同じ問題が発生し、リクエストを送信する際にデフォルトの Content-Type が使用されます。
$.post("../product/save", model, function (res) {
//res contains the markup returned by the partial view
console.log('res', res);
});
webapiでPOSTを扱うのは厄介です! すでに正しい答えがある。
GETを扱うのは些細なことなので、特にPOSTに焦点を当てます。WebapisでGETの問題を解決するために検索しまくる人はあまりいないと思います。とにかく...
MVCのWeb APIで、一般的なHTTP動詞以外のカスタムアクションメソッド名を使うには?- 複数の投稿を実行する?- 複数の単純な型を投稿する?- jQuery経由で複雑な型を投稿する?
それなら、以下の解決策が役に立つかもしれません:
まず、ウェブAPIでカスタムアクションメソッドを使用するには、次のようにウェブAPIルートを追加します:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}");
}
そして、次のようなアクションメソッドを作成します:
[HttpPost]
public string TestMethod([FromBody]string value)
{
return "Hello from http post web api controller: " + value;
}
次に、ブラウザのコンソールから次のjQueryを実行します。
$.ajax({
type: 'POST',
url: 'http://localhost:33649/api/TestApi/TestMethod',
data: {'':'hello'},
contentType: 'application/x-www-form-urlencoded',
dataType: 'json',
success: function(data){ console.log(data) }
});
次に、複数の投稿を実行する方法は簡単で、複数のアクションメソッドを作成し、[HttpPost]アトリビュートで装飾します。カスタム名などを割り当てるには、[ActionName("MyAction")]を使います。jQueryについては後述します。
3つ目は、まず1つのアクションで複数のSIMPLE型を投稿することはできません。 さらに、(クエリストリングやRESTスタイルでパラメータを渡すのとは別に)単一の単純な型を投稿するにも特別な形式があります。 この点は、私がRestクライアント(FiddlerやChromeのAdvanced REST client extensionなど)で頭を叩き、5時間近くウェブを探し回ったポイントだった。リンクが切れるかもしれないので、関連する内容を引用する!
Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"[email protected]"}
追記:特殊な構文にお気づきですか?
http://forums.asp.net/t/1883467.aspx?The+received+value+is+null+when+I+try+to+++Post+to+my+Web+Api
とにかく、その話は終わりにしよう。次に進もう:
第四に、複雑な型をjQueryで投稿する場合、もちろん$.ajax()がその役割を果たすことになる:
アクションメソッドが、idとnameを持つPersonオブジェクトを受け入れるとしよう。そこで、javascriptから
var person = { PersonId:1, Name:"James" }
$.ajax({
type: 'POST',
url: 'http://mydomain/api/TestApi/TestMethod',
data: JSON.stringify(person),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function(data){ console.log(data) }
});
アクションは次のようになります:
[HttpPost]
public string TestMethod(Person person)
{
return "Hello from http post web api controller: " + person.Name;
}
上記のすべて、私のために働いた!!ありがとう!
私は今、これで遊んでいて、ちょっと奇妙な結果を発見しました。例えば、C#のクラスに以下のようなパブリック・プロパティがあるとする:
public class Customer
{
public string contact_name;
public string company_name;
}
のようなプロパティがあるとすると、Shyjuが提案したJSON.stringifyのトリックを実行し、次のように呼び出す必要があります:
var customer = {contact_name :"Scott",company_name:"HP"};
$.ajax({
type: "POST",
data :JSON.stringify(customer),
url: "api/Customer",
contentType: "application/json"
});
しかし、このようにクラスにゲッターとセッターを定義すると、次のようになります:
public class Customer
{
public string contact_name { get; set; }
public string company_name { get; set; }
}
のように定義すれば、もっとシンプルに呼び出すことができます:
$.ajax({
type: "POST",
data :customer,
url: "api/Customer"
});
これはHTTPヘッダーを使用します:
Content-Type:application/x-www-form-urlencoded
何が起こっているのかよくわからないが、フレームワークのバグ(機能?)のようだ。おそらく、異なるバインディング・メソッドが異なるアダプタを呼び出していて、application/json用のアダプタはパブリック・プロパティで動作しますが、フォーム・エンコード・データ用のアダプタは動作しません。
しかし、どちらがベストプラクティスなのか、私にはわかりません。
-contentType: 'application / json'。
以下は、asp.net Web apiへのajaxポストコールを作成するためのgive jqueryコードです。
<。!-スニペットを開始:js hide:false -->。
var product =
JSON.stringify({
productGroup: "Fablet",
productId: 1,
productName: "Lumia 1525 64 GB",
sellingPrice: 700
});
$.ajax({
URL: 'http://localhost/api/Products',
type: 'POST',
contentType: 'application/json',
data: product,
success: function (data, status, xhr) {
alert('Success!');
},
error: function (xhr, status, error) {
alert('Update Error occurred - ' + error);
}
});
<。!-終了スニペット-->。
WebAPIサービスが、合格しているJSONに一致する構造を持つ強く入力されたオブジェクトを期待していることを確認してください。 そして、あなたが投稿しているJSONを文字列化することを確認してください。
これが私のJavaScriptです(AngluarJSを使用):
$scope.updateUserActivity = function (_objuserActivity) {
$http
({
method: 'post',
url: 'your url here',
headers: { 'Content-Type': 'application/json'},
data: JSON.stringify(_objuserActivity)
})
.then(function (response)
{
alert("success");
})
.catch(function (response)
{
alert("failure");
})
.finally(function ()
{
});
これが私のWebAPIコントローラーです。
[HttpPost]
[AcceptVerbs("POST")]
public string POSTMe([FromBody]Models.UserActivity _activity)
{
return "hello";
}
xml -Web API 2の代わりにjson形式でデータを返すコードに従ってください:-。
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
@model MVCClient.Models.ProductDetails
@{
ViewBag.Title = "ProductDetails";
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#Save").click(function () {
var ProductDetails = new Object();
ProductDetails.ProductName = $("#txt_productName").val();
ProductDetails.ProductDetail = $("#txt_desc").val();
ProductDetails.Price= $("#txt_price").val();
$.ajax({
url: "http://localhost:24481/api/Product/addProduct",
type: "Post",
dataType:'JSON',
data:ProductDetails,
success: function (data) {
alert('Updated Successfully');
//window.location.href = "../Index";
},
error: function (msg) { alert(msg); }
});
});
});
</script>
<h2>ProductDetails</h2>
<form id="form1" method="post">
<fieldset>
<legend>ProductDetails</legend>
<div class="editor-label">
@Html.LabelFor(model => model.ProductName)
</div>
<div class="editor-field">
<input id="txt_productName" type="text" name="fname">
@Html.ValidationMessageFor(model => model.ProductName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ProductDetail)
</div>
<div class="editor-field">
<input id="txt_desc" type="text" name="fname">
@Html.ValidationMessageFor(model => model.ProductDetail)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
<input id="txt_price" type="text" name="fname">
@Html.ValidationMessageFor(model => model.Price)
</div>
<p>
<input id="Save" type="button" value="Create" />
</p>
</fieldset>
</form>
<div>
@Html.ActionLink("Back to List", "Index")
</div>
</form>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
マイクロソフトはこれを行う良い例を示しました:
https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-1。
まず、リクエストを検証します。
if (ModelState.IsValid)
シリアル化されたデータを使用します。
Content = new StringContent(update.Status)
ここで「ステータス」は複雑なタイプのフィールドです。 シリアル化は.NETによって行われ、心配する必要はありません。
1)クライアント側では、http.postリクエストを以下のような文字列で送信できます。
var IndexInfo = JSON.stringify(this.scope.IndexTree);
this.$http.post('../../../api/EvaluationProcess/InsertEvaluationProcessInputType', "'" + IndexInfo + "'" ).then((response: any) => {}
2)次に、Web apiコントローラーでそれを脱線させることができます。
public ApiResponce InsertEvaluationProcessInputType([FromBody]string IndexInfo)
{
var des = (ApiReceivedListOfObjects<TempDistributedIndex>)Newtonsoft.Json.JsonConvert.DeserializeObject(DecryptedProcessInfo, typeof(ApiReceivedListOfObjects<TempDistributedIndex>));}
3)ApiReivedListOfObjectsクラスは以下のとおりです。
public class ApiReceivedListOfObjects<T>
{
public List<T> element { get; set; }
}
4)ステップ2のJsonConvert.DeserializeObjectコマンドの前に、シリアル化された文字列(ここではIndexInfo)が構造の下のようになることを確認してください。
var resp = @"
{
""element"": [
{
""A"": ""A Jones"",
""B"": ""500015763""
},
{
""A"": ""B Smith"",
""B"": ""504986213""
},
{
""A"": ""C Brown"",
""B"": ""509034361""
}
]
}";