WebAPIをRCにアップグレードしてから、WebAPIでPOSTを呼び出すときに、ちょっと変わった問題が発生しました。私は新しいプロジェクトで生成された基本バージョンにもどってきました。そう:
public void Post(string value)
{
}
そしてFiddlerから電話をかける:
Header:
User-Agent: Fiddler
Host: localhost:60725
Content-Type: application/json
Content-Length: 29
Body:
{
"value": "test"
}
私がデバッグするとき、文字列 "value"は決して割り当てられていません。常にNULLです。この問題を抱えている人はいますか?
(私は最初にもっと複雑なタイプの問題を見ました)
この問題はASP.NET MVC 4だけではなく、RCインストール後の新しいASP.NET MVC 3プロジェクトでも同じ問題が発生します。
パラメータが1つしかないため、ここで提案したように、[FromBody]
属性でそれを装飾するか、プロパティとして値を持つDTOを受け入れるようにメソッドを変更することができます。 MVC4 RC WebApiパラメータバインディング
更新:公式ASP.NETサイトは今日、素晴らしい説明を付けて更新されました: https://docs.Microsoft.com/ja-jp/aspnet/web-api/overview/advanced/sending-html-form-data-パート1
一言で言えば、本体に単一の単純型を送るときは、等号(=)を前に付けた値だけを送ります。体:
=test
私は今日このことで頭をかいています。
私の解決策は、基本的にHTTPスタックを上に移動して、[FromBody]
をHttpRequestMessage
に変更することです。
私の場合は、jsonで圧縮されたワイヤーを介してデータを送信しています。これはすべてAndroidアプリからです。
私のWebエンドポイントのオリジナルのシグネチャは、([FromBody]
を使って)次のようになりました。
この問題に対する私の修正は、私のエンドポイントの署名にHttpRequestMessage
を使うことに戻ることでした。
このコード行を使用して投稿データにアクセスできます。
これは機能し、あなたは生の手付かずの投稿データにアクセスすることができます。文字列の先頭に=記号を付けたり、content-typeを変更したりする必要がありません。
余談ですが、私は最初に、コンテンツタイプを "Content-Type:application/x-www-form-urlencoded"に変更するという上記の答えの1つに従うことを試みました。生データの場合、これは+文字を削除するので不適切なアドバイスです。
したがって、「MQ0AAB + LCAAAAAA」のように始まるbase64ストリングは、「MQ0AAB LCAAAAAA」のようになります。あなたが望むものではありません。
HttpRequestMessage
を使用するもう1つの利点は、エンドポイント内からすべてのhttpヘッダーにアクセスできることです。
私はFiddlerを使ってこれを起こしたばかりです。問題は、私がContent-Type
を指定していないということでした。
POSTリクエストにContent-Type
のヘッダーを含めてみてください。
Content-Type: application/x-www-form-urlencoded
あるいは、下記のコメントのとおり、JSONヘッダを含める必要があるかもしれません。
Content-Type: application/json
私はこの問題にも遭遇しました、そしてこれは私が私の問題を解決した方法です
webAPIコード:
public void Post([FromBody] dynamic data)
{
string value = data.value;
/* do stuff */
}
クライアントコード:
$.post( "webapi/address", { value: "some value" } );
私はPostman
を使用していました、そして私は同じ間違いをしていました..文字列の代わりにjsonオブジェクトとしてvalue
を渡します
{
"value": "test"
}
Apiパラメータが文字列型の場合、明らかに上記の1つは間違っています。
そのため、api本体内で文字列を二重引用符で囲むだけで済みます。
"test"
データモデルとして機能するクラスを作成してから、データモデルクラスのプロパティと一致するプロパティを持つJSONオブジェクトを送信します。 (注:私はこれをテストしました、そしてそれは私が今日ダウンロードした最新のMVC 4 RC 2012で動きます)。
public HttpResponseMessage Post(ValueModel model)
{
return Request.CreateResponse<string>(HttpStatusCode.OK, "Value Recieved: " + model.Value);
}
public class ValueModel
{
public string Value { get; set; }
}
以下のJSONオブジェクトはHTTP-POSTボディで送信され、content-typeはapplication/jsonです。
{ "value": "In MVC4 Beta you could map to simple types like string, but testing with RC 2012 I have only been able to map to DataModels and only JSON (application/json) and url-encoded (application/x-www-form-urlencoded body formats have worked. XML is not working for some reason" }
データモデルクラスを作成する必要があるのは、単純な値はurlパラメータからのものであると想定され、単一の複雑な値は本体からのものと想定されるためです。彼らは[FromBody]
と[FromUrl]
属性を持っています、しかし[FromBody] string value
を使うことはまだ私にとってはうまくいきませんでした。まだ多くのバグを解決しているようですので、今後変更されると思います。
編集:本文で動作するようにXMLを取得しました。デフォルトのXMLシリアライザは、XmlSerializerではなくDataContractSerializerに変更されました。 Global.asaxファイルに次の行を追加することで、この問題は解決しました( 参照 )
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
何度か試した後、私はデフォルトの振る舞いは正しいと思いますし、ハッキングするものは何もないと思います。
唯一のトリックは以下の通りです:もしあなたのポストメソッドの引数が以下のようにstring
であれば、 二重引用符で囲まれたプレーンな文字列 を(ajaxやpostmanを使っているときに)送ってください。
//send "{\"a\":1}" in body to me, note the outer double quotes
[HttpPost("api1")]
public String PostMethod1([FromBody]string value)
{
return "received " + value; // "received {\"a\":1}"
}
それ以外の場合は、投稿本体にjson文字列を - 外側の二重引用符なし およびエスケープした内側の引用符で送ると、モデルクラス(引数の型)に解析できます(例:{"a":1, "b":2}
)。
public class MyPoco{
public int a;
public int b;
}
//send {"a":1, "b":2} in body to me
[HttpPost("api2")]
public String PostMethod2([FromBody]MyPoco value)
{
return "received " + value.ToString(); //"received your_namespace+MyPoco"
}
私は数分間この問題の解決策を今探していたので、私は私の解決策を共有するつもりです。
モデルを投稿する場合、あなたのモデルは空の/デフォルトコンストラクタを持つ必要があります。そうでなければ、明らかにモデルは作成できません。リファクタリングしながら注意してください。 ;)
これは私のために働いた:
JQuery/Ajaxから渡すすべての属性のプロパティを使ってC#DTOクラスを作成します。
public class EntityData
{
public string Attr1 { get; set; }
public string Attr2 { get; set; }
}
Web APIメソッドを定義します。
[HttpPost()]
public JObject AddNewEntity([FromBody] EntityData entityData)
{
このようにWeb APIを呼び出します。
var entityData = {
"attr1": "value1",
"attr2": "value2"
};
$.ajax({
type: "POST",
url: "/api/YOURCONTROLLER/addnewentity",
async: true,
cache: false,
data: JSON.stringify(entityData),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
...
}
});
私と同じようにSwaggerまたはPostmanと同じ問題を抱えている人のために"ContentType"が指定されていても、投稿で単純な属性を文字列として渡している場合は、null値が返されます。
ちょうど通り過ぎる:
MyValue
Nullとしてコントローラに入ります。
しかしあなたが合格するならば:
"MyValue"
値は正しくなります。
引用符はここで違いを生んだ。もちろん、これはSwaggerとPostman専用です。たとえば、Angularを使用するフロントエンドアプリでは、これはフレームワークによって自動的に解決されるはずです。
私は同じ問題を抱えていて、コンテンツタイプを "application/json"に変更しても問題が解決しないことを発見しました。しかし、 "application/json; charset = utf-8"は動作しました。
私のWeb APIメソッドのリクエストオブジェクトが常にnullであるという同様の問題がありました。コントローラのアクション名の先頭に "Get"が付いているので、Web APIではこれがPOSTではなくHTTP GETとして扱われることに気付きました。コントローラアクションの名前を変更した後、意図したとおりに動作するようになりました。
私の場合の問題は、パラメータが文字列であってオブジェクトではないということでした。Newsoft.JsonのJObjectになるようにパラメータを変更しました。
送信されたJSONについて確信がある場合は、APIを慎重に追跡する必要があります。
Microsoft.AspNet.WebApi.Tracing
パッケージをインストールするWebApiConfig
メソッド内のRegister
クラスにconfig.EnableSystemDiagnosticsTracing();
を追加します。デバッグ出力を見てください、おそらく無効なModelState
ログエントリを見つけるでしょう。
ModelState
が無効な場合、本当の原因はErrors
にあります。
誰もそのような例外を推測することさえできません。
Could not load file or Assembly 'Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located Assembly's manifest definition does not match the Assembly reference. (Exception from HRESULT: 0x80131040)
Xml FormatterまたはJSON FormatterにDataContractSerializerを使用している場合は、それを削除する必要があります。私は自分のWebApiConfigファイルでこれを持っていました:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.UseDataContractJsonSerializer = true;
}
単にjsonFormatter.UseDataContractJsonSerializer = true;
をコメントアウトするだけで、私の入力パラメータはもうnullではありません。手がかりを与えてくれた 'Despertar'に感謝します。
行を追加する
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
global.asax.csの関数protected void Application_Start()
の終わりに私にとってASP.NET MVC3で同様の問題を修正しました。
Angularでは、次の形式でデータを渡すことができました。
data: '=' + JSON.stringify({ u: $scope.usrname1, p: $scope.pwd1 }),
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' }
そしてWeb API Controlerでは:
[HttpPost]
public Hashtable Post([FromBody]string jsonString)
{
IDictionary<string, string> data = JsonConvert.DeserializeObject<IDictionary<string, string>>(jsonString);
string username = data["u"];
string pwd = data["p"];
......
あるいは、私はこのようなJSONデータを投稿することもできます。
data: { PaintingId: 1, Title: "Animal show", Price: 10.50 }
そして、コントローラでは、このようなクラス型を受け入れます。
[HttpPost]
public string POST(Models.PostModel pm)
{
....
}
APIに確立されたパブリッククラスがある場合はJSONを投稿し、そうでなければ '=' + JSON.stringify({..:...、..:...})を投稿
パーティーには少し時間がかかりますが、コントローラを使用するときに渡されるNULL値に出会う人は誰でも単にPOSTリクエストの前に "="を追加します。
application/jsonContent-Typeを使用したときにも、コントローラーはNULL値を渡しました。下記の「application/x-www-form-urlencode」Content-Typeに注意してください。ただし、APIからの戻り型は "application/json"です。
public static string HttpPostRequest(string url, Dictionary<string, string> postParameters)
{
string postData = "=";
foreach (string key in postParameters.Keys)
{
postData += HttpUtility.UrlEncode(key) + "="
+ HttpUtility.UrlEncode(postParameters[key]) + ",";
}
HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
myHttpWebRequest.Method = "POST";
byte[] data = System.Text.Encoding.ASCII.GetBytes(postData);
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.ContentLength = data.Length;
Stream requestStream = myHttpWebRequest.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
Stream responseStream = myHttpWebResponse.GetResponseStream();
StreamReader myStreamReader = new StreamReader(responseStream, System.Text.Encoding.Default);
string pageContent = myStreamReader.ReadToEnd();
myStreamReader.Close();
responseStream.Close();
myHttpWebResponse.Close();
return pageContent;
}
私にとって最良の解決策は、以下のようにフルHTTPにすることです。
[Route("api/open")]
[HttpPost]
public async Task<string> open(HttpRequestMessage request)
{
var json = await request.Content.ReadAsStringAsync();
JavaScriptSerializer jss = new JavaScriptSerializer();
WS_OpenSession param = jss.Deserialize<WS_OpenSession>(json);
return param.sessionid;
}
その後、投稿本文で期待するオブジェクトに文字列を逆シリアル化します。私にとっては、WS_OpenSessionはsessionid、user、keyを含むクラスです。
そこからparamオブジェクトを使用してそのプロパティにアクセスできます。
とても効果的です。
私はこのURLからの発言をしました:
データ型を再確認してください。 dotnetモデルバインダーはfloatを整数に変換しません(そして私は他の関連概念を想定しています)これにより、モデル全体が拒否されます。
あなたがこのようなJSONを持っているならば:
{
"shoeSize": 10.5
}
しかし、あなたのC#モデルはこのようになります:
class Shoe{
public int shoeSize;
}
モデルバインダーはモデルを拒否し、nullになります。
パラメータとしてnullを取得するという同じ問題がありましたが、ラージオブジェクトに関連していました。問題はIIS max lengthに関連していることがわかりました。それはweb.configで設定することができます。
<system.web>
<httpRuntime targetFramework="4.7" maxRequestLength="1073741824" />
</system.web>
なぜWeb APIがエラーを抑制し、私のAPIにnullオブジェクトを送信するのでしょうか。 Microsoft.AspNet.WebApi.Tracingを使用してエラーを見つけました。
私はこれがこの質問に対する答えではないことを知っています、しかし私は私の問題に対する解決策を探すときそれに出会いました。
私の場合、複合型はバインドされていませんでしたが、POSTは実行していませんでした。クエリ文字列パラメータを使用してGETを実行していました。解決策は[FromUri]を引数に追加することでした。
public class MyController : ApiController
{
public IEnumerable<MyModel> Get([FromUri] MyComplexType input)
{
// input is not null as long as [FromUri] is present in the method arg
}
}
私はこれにかなり遅れていますが、同様の問題を抱えていて、ここで多くの答えを調べて背景を取得した後、1つ以上のパラメータをWeb API 2 Actionに返す最も簡単で軽量なソリューションが見つかりました次のとおりです。
これは、以下を参照しない場合、正しいルーティングでWeb APIコントローラー/アクションをセットアップする方法を知っていることを前提としています: https://docs.Microsoft.com/en-us/aspnet/web-api/overview/getting -started-with-aspnet-web-api/tutorial-your-first-web-api 。
まずコントローラーアクション、このソリューションにはNewtonsoft.Jsonライブラリも必要です。
[HttpPost]
public string PostProcessData([FromBody]string parameters) {
if (!String.IsNullOrEmpty(parameters)) {
JObject json = JObject.Parse(parameters);
// Code logic below
// Can access params via json["paramName"].ToString();
}
return "";
}
JQueryを使用するクライアント側
var dataToSend = JSON.stringify({ param1: "value1", param2: "value2"...});
$.post('/Web_API_URI', { '': dataToSend }).done(function (data) {
console.debug(data); // returned data from Web API
});
私が見つけた重要な問題は、単一の全体的なパラメーターのみをWeb APIに送信し、値が{ '': dataToSend }
以外の名前になっていないことを確認することでした。
これにより、1つまたは複数のパラメーターをJSON構造でWeb APIに送信でき、複雑なデータを処理するためにサーバー側で追加のオブジェクトを宣言する必要はありません。また、JObjectを使用すると、渡されたすべてのパラメーターを動的に反復処理して、パラメーターが時間の経過とともに変化した場合にスケーラビリティを容易にすることができます。うまくいけば、それは私のように苦労していた誰かを助けます。
複雑な型の場合、Web APIはメディア型フォーマッタを使用してメッセージ本文から値を読み込もうとします。
モデルクラスを装飾する[Serializable]
属性があるかどうかを確認してください。
機能するかどうか確認するために、属性を削除します。これは私のために働きました。
私がMVC 6に渡す単純なJSONオブジェクトを処理するために見つけた最も簡単な方法は、NewtonSoft jObjectのようなpostパラメータの型を取得することです。
public ActionResult Test2([FromBody] jObject str)
{
return Json(new { message = "Test1 Returned: "+ str }); ;
}
私はフィドラーでも同じ問題を抱えていました。リクエストヘッダに既にContent-Type: application/json; charset=utf-8
またはContent-Type: application/json
があります。
私のリクエストボディもプレーンな文字列でした、そして私がFiddlerで書いたのは{'controller':'ctrl'}
です。これにより、私のPOSTメソッドの文字列パラメータはnull
になりました。
修正:必ず引用符を使用して、文字列を示してください。つまり、私は"{'controller':'ctrl'}"
を書くことによってそれを修正しました。 (注:JSONを書くときは、必ずアポストロフィを使用するか、または次のように引用符をエスケープしてください。"{\"controller\":\"ctrl\"}"
).
どのような種類の値を投稿するかは問題ではなく、文字列として取得するために引用符で囲みます。複合型ではありません。
javaScript:
var myData = null, url = 'api/' + 'Named/' + 'NamedMethod';
myData = 7;
$http.post(url, "'" + myData + "'")
.then(function (response) { console.log(response.data); });
myData = "some sentence";
$http.post(url, "'" + myData + "'")
.then(function (response) { console.log(response.data); });
myData = { name: 'person name', age: 21 };
$http.post(url, "'" + JSON.stringify(myData) + "'")
.then(function (response) { console.log(response.data); });
$http.post(url, "'" + angular.toJson(myData) + "'")
.then(function (response) { console.log(response.data); });
c#:
public class NamedController : ApiController
{
[HttpPost]
public int NamedMethod([FromBody] string value)
{
return value == null ? 1 : 0;
}
}
Postmanのjsonクエリにこの文字「\」がある場合、json文字列値に文字「\」が必要な場合はエスケープする必要があります。
例:
{
"path": "C:\\Users\\Test\\Documents\\test.txt"
}
本体内の単一のパラメーターをWebAPIに渡すことで、このコードは正しく機能します$.post(url, { '': productId }
そしてそれを実際にとらえて[HttpPost] public ShoppingCartAddRemoveViewModel Delete([FromBody]string value)
キーはマジックワードの「値」を使うことです。これはint型、あるいは何らかのプリミティブ型でもあります。 content-typeやheaderの修正に関係なく、このコードはMVCのpostアクションでは動作しません。
私の場合は、Newtonsoftの[JsonObject(MemberSerialization.OptOut)]
属性を使ってパラメータクラスを装飾することでうまくいきました。
例えば:
[HttpPost]
[Route("MyRoute")]
public IHttpActionResult DoWork(MyClass args)
{
...
}
[JsonObject(MemberSerialization.OptOut)]
public Class MyClass
{
...
}
JSON.stringify(...)私の問題を解決しました
API:
[HttpPost]
public bool UpdateTicketStatus(Ticket t)
{
string Id=t.Id;
}
モデル:
public class Ticket
{
public int Id { get; set; }
public string AssignedTo { get; set; }
public string State { get; set; }
public string History { get; set; }
}
ポストマンツールを使用して、以下のように生データを含むJSONとしてコンテンツを送信します。できます
{「ID」:「169248」、「割り当て先」:「xxxx」、「状態」:「コミット」、「履歴」:「test1」}
Corsが有効になっていることを確認してください。
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class TicketController : ApiController
{
}
nullを送信してnullではないプロパティにバインドするなどのさまざまな理由による、ASP.NET Coreでの同様の問題とその他の考えられる原因は、ASP.NETバインド(サイレント)失敗です:
{
"prop1":1139357,
"prop2":1139356,
"items":[
{"key":"15","someprop":34,"notnullprop":null},
{"key":"16","someprop":34,"notnullprop":null},
{"key":"22","someprop":34,"notnullprop":null}]
}
そのような場合、例外がスローされることはなく、たとえこれがオブジェクト階層の奥深くで起こっても、モデル全体はnullになります。
お役に立てれば。
別のコメントや他のフォーラムを見て、私はいくつかのスニペットを混ぜると私のために、このコードは動作します...
...コントローラーで
public HttpResponseMessage Post([FromBody] string jsonData)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, jsonData);
try
{
string jsonString = jsonData.ToString();
JArray jsonVal = JArray.Parse(jsonString) as JArray;
dynamic mylist= jsonVal;
foreach (dynamic myitem in mylist)
{
string strClave=string.Empty;
string strNum=string.Empty;
string strStatus=string.Empty;
strClave = myitem.clave;
strNum=myitem.num;
strStatus = myitem.status;
}
... WebApiConfig.csでは、[FromBody]変数にNULL値を避けるためにこの行を含めますvar jsonFormatter = config.Formatters.OfType()。First();
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First(); /*this line makes no more null when use [FromBody]*/
}
....クライアント側では、最も重要なことは、シリアル化されたデータの前に等号を連結することです(文字列json = "=" + SerialData;)
seralize用に使っている
System.Web.Script.Serialization.JavaScriptSerializer serializer = new
System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary<string, object> row;
foreach (DataRow dr in DsCnx.Tables[0].Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in DsCnx.Tables[0].Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
SerialData= serializer.Serialize(rows);
PostRequest("http://localhost:53922/api/Demo", SerialData);
これは私のPostRequest関数です、ここで私が使うコンテンツタイプはhttpWebRequest.ContentType = "application/x-www-form-urlencoded; charset = utf-8";:
private static string PostRequest(string url, string SerialData)
{
string result = String.Empty;
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "=" + SerialData;
streamWriter.Write(json);
streamWriter.Flush();
streamWriter.Close();
}
try
{
using (var response = httpWebRequest.GetResponse() as HttpWebResponse)
{
if (httpWebRequest.HaveResponse && response != null)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
}
}
}
catch (WebException e)
{
if (e.Response != null)
{
using (var errorResponse = (HttpWebResponse)e.Response)
{
using (var reader = new StreamReader(errorResponse.GetResponseStream()))
{
string error = reader.ReadToEnd();
result = error;
}
}
}
}
return result.ToString();
}
ここで私は参考のために1つのコード例を見つけたリンクです:
https://blog.codenamed.nl/2015/05/12/why-your-frombody-parameter-is-always-null/
[FromBody]アノテーションを付けても、メソッドへのパラメータとしてDtoオブジェクトがあり、それでもデータを取得できない場合は、DTOのプロパティとフィールドを調べ始めます。
私のDTOが無効になるという同じ問題がありました。その理由の1つは、プロパティの1つがシリアライズできない :(を指すことであり、これによってメディアフォーマッタがデータを解析できないため、オブジェクトは常にnullになりました。それが他の人にも役立つことを願っています
問題は、あなたのアクションメソッドが単純な型、すなわち文字列パラメータ値を期待しているということです。あなたが提供しているものは物です。
あなたの問題には2つの解決策があります。
"value"プロパティを使用して単純なクラスを作成し、そのクラスをパラメータとして使用します。それはあなたのparamオブジェクトの "values"プロパティに与えます。
文字列値 "test"を渡すだけで動作します。