웹 API POST 본문 객체는 항상 null입니다.
나는 여전히 웹 API를 배우고 있으므로 내 질문이 어리석은 것처럼 들리면 용서하십시오.
나는 이것을 내 StudentController
:
public HttpResponseMessage PostStudent([FromBody]Models.Student student)
{
if (DBManager.createStudent(student) != null)
return Request.CreateResponse(HttpStatusCode.Created, student);
else
return Request.CreateResponse(HttpStatusCode.BadRequest, student);
}
이것이 작동하는지 테스트하기 위해 Google 크롬의 확장 "Postman"을 사용하여 HTTP POST 요청을 구성하여 테스트합니다.
이것은 내 원시 POST 요청입니다.
POST /api/Student HTTP/1.1
Host: localhost:1118
Content-Type: application/json
Cache-Control: no-cache
{"student": [{"name":"John Doe", "age":18, "country":"United States of America"}]}
"student"
은 객체 여야하지만 애플리케이션을 디버깅 할 때 API는 학생 객체를 수신하지만 콘텐츠는 항상 NULL
.
FromBody는 입력 POST 값이 기본 유형이 아닌 경우 매개 변수가 널이 아니도록 특정 형식이어야한다는 점에서 이상한 속성입니다. (여기 학생)
{"name":"John Doe", "age":18, "country":"United States of America"}
json으로 요청을 시도하십시오 .[FromBody]
속성을 제거하고 해결책을 시도하십시오. 기본이 아닌 유형에 대해 작동해야합니다. (학생)- 와
[FromBody]
속성, 다른 옵션의 값을 전송하는=Value
것이 아니라, 포맷key=value
형식입니다. 이는의 키 값이student
빈 문자열이어야 함을 의미합니다 .
학생 클래스에 대한 사용자 지정 모델 바인더를 작성하고 사용자 지정 바인더로 매개 변수를 지정하는 다른 옵션도 있습니다.
이제 몇 분 동안 내 문제에 대한 해결책을 찾고 있었으므로 내 해결책을 공유하겠습니다.
모델에는 빈 / 기본 생성자가 있어야합니다. 그렇지 않으면 모델을 만들 수 없습니다. 리팩토링하는 동안주의하십시오. ;)
이 문제로 몇 시간을 보냈습니다 ... :( POST 매개 변수 개체 선언에서 Getter 및 setter가 필요합니다. 특수 요청 형식이 필요하므로 간단한 데이터 개체 (string, int, ...)를 사용하지 않는 것이 좋습니다.
[HttpPost]
public HttpResponseMessage PostProcedure(EdiconLogFilter filter){
...
}
다음과 같은 경우 작동하지 않습니다.
public class EdiconLogFilter
{
public string fClientName;
public string fUserName;
public string fMinutes;
public string fLogDate;
}
다음과 같은 경우 잘 작동합니다.
public class EdiconLogFilter
{
public string fClientName { get; set; }
public string fUserName { get; set; }
public string fMinutes { get; set; }
public string fLogDate { get; set; }
}
이것은 조금 오래된 것이고 내 대답은 마지막 장소로 내려갈 것이지만 그래도 내 경험을 공유하고 싶습니다.
모든 제안을 시도했지만 여전히 PUT [FromBody]에 동일한 "null"값이 있습니다.
마지막으로 JSON이 내 Angular 개체의 EndDate 속성을 직렬화하는 동안 날짜 형식에 관한 모든 것을 발견했습니다.
오류가 발생하지 않았으며 빈 FromBody 개체를 받았습니다 ....
요청의 JSON 객체 값이 서비스에서 예상 한 것과 동일한 유형이 아닌 경우 [FromBody]
인수는 null
.
예를 들어 json 의 age 속성에 float
값 이있는 경우 :
"연령": 18.0
하지만 API 서비스는 int
"나이": 18
다음 student
될 것입니다 null
. (널 참조 검사가 없으면 응답에 오류 메시지가 전송되지 않습니다).
Postman을 사용하는 경우 다음을 확인하십시오.
- "Content-Type"헤더를 "application / json"으로 설정했습니다.
- 본문을 "원시"로 보내고 있습니다.
- [FromBody]를 사용하는 경우 매개 변수 이름을 어디에도 지정할 필요가 없습니다.
어리석게 JSON을 양식 데이터로 보내려고했는데 ...
요약 : [FromBody]를 사용하지 말고 더 나은 오류 처리로 자신의 버전을 롤링하십시오. 아래에 주어진 이유.
다른 답변은이 문제의 여러 가능한 원인을 설명합니다. 그러나 근본 원인은 [FromBody]
단순히 끔찍한 오류 처리가 있기 때문에 프로덕션 코드에서는 거의 쓸모가 없습니다.
For example, one of the most typical reasons for the parameter to be null
is that the request body has invalid syntax (e.g., invalid JSON). In this case, a reasonable API would return 400 BAD REQUEST
, and a reasonable web framework would do this automatically. However, ASP.NET Web API is not reasonable in this regard. It simply sets the parameter to null
, and the request handler then needs "manual" code to check if the parameter is null
.
따라서 여기에 제공된 많은 답변은 오류 처리와 관련하여 불완전하며 버그가 있거나 악의적 인 클라이언트는 잘못된 요청을 보내 서버 측에서 예상치 못한 동작을 유발할 수 있습니다. 이는 최상의 경우 NullReferenceException
어딘가에 던져 잘못된 결과를 반환합니다. 상태 500 INTERNAL SERVER ERROR
또는 더 나쁜 것은 예기치 않은 작업을 수행하거나 충돌하거나 보안 취약성을 노출합니다.
적절한 해결책은 [FromBody]
적절한 오류 처리를 수행하고 적절한 상태 코드를 반환 하는 사용자 정의 " "속성 을 작성하는 것입니다 . 이상적으로는 클라이언트 개발자를 지원하기위한 일부 진단 정보가 포함됩니다.
도움이 될 수있는 솔루션 (아직 테스트되지 않음)은 다음과 같이 필수 매개 변수를 만드는 것입니다. https://stackoverflow.com/a/19322688/2279059
다음과 같은 서투른 솔루션도 작동합니다.
// BAD EXAMPLE, but may work reasonably well for "internal" APIs...
public HttpResponseMessage MyAction([FromBody] JObject json)
{
// Check if JSON from request body has been parsed correctly
if (json == null) {
var response = new HttpResponseMessage(HttpStatusCode.BadRequest) {
ReasonPhrase = "Invalid JSON"
};
throw new HttpResponseException(response);
}
MyParameterModel param;
try {
param = json.ToObject<MyParameterModel>();
}
catch (JsonException e) {
var response = new HttpResponseMessage(HttpStatusCode.BadRequest) {
ReasonPhrase = String.Format("Invalid parameter: {0}", e.Message)
};
throw new HttpResponseException(response);
}
// ... Request handling goes here ...
}
이것은 적절한 오류 처리를 수행하지만 덜 선언적입니다. 예를 들어 Swagger를 사용하여 API를 문서화하는 경우 매개 변수 유형을 알지 못합니다. 즉, 매개 변수를 문서화하기 위해 몇 가지 수동 해결 방법을 찾아야합니다. 이것은 단지 무엇을 [FromBody]
해야하는지 설명하기위한 것 입니다.
편집 : 덜 서투른 해결책은 https://stackoverflow.com/a/38515689/2279059 를 확인하는 것입니다 ModelState
.
편집 : 그것은 그 표시 ModelState.IsValid
하나가, 설정을 기대하는 것처럼 아닌 false
사용하는 경우 JsonProperty
와 Required = Required.Always
및 매개 변수가 누락되어있다. 그래서 이것도 쓸모가 없습니다.
그러나 제 생각에는 모든 요청 핸들러 에 추가 코드를 작성해야하는 솔루션 은 허용되지 않습니다. 강력한 직렬화 기능이있는 .NET과 같은 언어 및 ASP.NET Web API와 같은 프레임 워크에서 요청 유효성 검사는 자동으로 기본 제공되어야하며 Microsoft가 필요한 기본 제공 기능을 제공하지 않더라도 완전히 수행 할 수 있습니다. 도구.
또한 [FromBody]를 사용하려고했지만 입력이 변경되고 백엔드 서비스에 전달해야하므로 문자열 변수를 채우려 고했지만 항상 null이었습니다.
Post([FromBody]string Input])
그래서 동적 클래스를 사용하도록 메서드 서명을 변경 한 다음 문자열로 변환합니다.
Post(dynamic DynamicClass)
{
string Input = JsonConvert.SerializeObject(DynamicClass);
이것은 잘 작동합니다.
json serializer에 TRACING을 추가하면 문제가 발생할 때 무슨 일이 일어나는지 확인할 수 있습니다.
다음과 같은 디버그 출력을 표시하도록 ITraceWriter 구현을 정의하십시오.
class TraceWriter : Newtonsoft.Json.Serialization.ITraceWriter
{
public TraceLevel LevelFilter {
get {
return TraceLevel.Error;
}
}
public void Trace(TraceLevel level, string message, Exception ex)
{
Console.WriteLine("JSON {0} {1}: {2}", level, message, ex);
}
}
그런 다음 WebApiConfig에서 다음을 수행하십시오.
config.Formatters.JsonFormatter.SerializerSettings.TraceWriter = new TraceWriter();
(아마 #if DEBUG로 포장)
제 경우에 문제는 DateTime
제가 보낸 물건이었습니다. DateTime
"yyyy-MM-dd"를 사용하여을 만들었 DateTime
으며 매핑중인 개체에 필요한 "HH-mm-ss"도 필요했습니다. 따라서 "00-00"을 추가하면 문제가 해결되었습니다 (이로 인해 전체 항목이 null 임).
3 일 동안 검색했지만 위의 솔루션 중 어느 것도 저에게 효과가 없었습니다.이 링크에서이 문제에 대한 또 다른 접근 방식을 찾았습니다. HttpRequestMessage
이 사이트의 솔루션 중 하나를 사용했습니다.
[HttpPost]
public async System.Threading.Tasks.Task<string> Post(HttpRequestMessage request)
{
string body = await request.Content.ReadAsStringAsync();
return body;
}
나는 같은 문제가 있었다.
제 경우 문제는 public int? CreditLimitBasedOn { get; set; }
제가 가진 재산에있었습니다.
my JSON had the value "CreditLimitBasedOn":true
when It should contain an integer. This property prevented the whole object being deserialized on my api method.
Maybe for someone it will be helpful: check the access modifiers for your DTO/Model class' properties, they should be public. In my case during refactoring domain object internals were moved to DTO like this:
// Domain object
public class MyDomainObject {
public string Name { get; internal set; }
public string Info { get; internal set; }
}
// DTO
public class MyDomainObjectDto {
public Name { get; internal set; } // <-- The problem is in setter access modifier (and carelessly performed refactoring).
public string Info { get; internal set; }
}
DTO is being finely passed to client, but when the time comes to pass the object back to the server it had only empty fields (null/default value). Removing "internal" puts things in order, allowing deserialization mechanizm to write object's properties.
public class MyDomainObjectDto {
public Name { get; set; }
public string Info { get; set; }
}
Check if JsonProperty
attribute is set on the fields that come as null - it could be that they are mapped to different json property-names.
I've hit this problem so many times, but actually, it's quite straightforward to track down the cause.
Here's today's example. I was calling my POST service with an AccountRequest
object, but when I put a breakpoint at the start of this function, the parameter value was always null
. But why ?!
[ProducesResponseType(typeof(DocumentInfo[]), 201)]
[HttpPost]
public async Task<IActionResult> Post([FromBody] AccountRequest accountRequest)
{
// At this point... accountRequest is null... but why ?!
// ... other code ...
}
To identify the problem, change the parameter type to string
, add a line to get JSON.Net
to deserialize the object into the type you were expecting, and put a breakpoint on this line:
[ProducesResponseType(typeof(DocumentInfo[]), 201)]
[HttpPost]
public async Task<IActionResult> Post([FromBody] string ar)
{
// Put a breakpoint on the following line... what is the value of "ar" ?
AccountRequest accountRequest = JsonConvert.DeserializeObject<AccountRequest>(ar);
// ... other code ...
}
Now, when you try this, if the parameter is still blank or null
, then you simply aren't calling the service properly.
However, if the string does contain a value, then the DeserializeObject
should point you towards the cause of the problem, and should also fail to convert your string into your desired format. But with the raw (string
) data which it's trying to deserialize, you should now be able to see what's wrong with your parameter value.
(In my case, we were calling the service with an AccountRequest
object which had been accidentally serialized twice !)
I used HttpRequestMessage and my problem got solved after doing so much research
[HttpPost]
public HttpResponseMessage PostProcedure(HttpRequestMessage req){
...
}
Just to add my history to this thread. My model:
public class UserProfileResource
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public UserProfileResource()
{
}
}
The above object couldn't be serialized in my API Controller and would always return null. The issue was with Id of type Guid: everytime I passed empty string as an Id (being naive that it will automatically be converted to Guid.Empty
) from my frontend I received null object as [FromBody]
paramether.
Solution was either to
- pass valid
Guid
value - or change
Guid
toString
Seems like there can be many different causes of this problem...
I found that adding an OnDeserialized
callback to the model class caused the parameter to always be null
. Exact reason unknown.
using System.Runtime.Serialization;
// Validate request
[OnDeserialized] // TODO: Causes parameter to be null
public void DoAdditionalValidatation() {...}
I had this problem in my .NET Framework Web API, because my model existed in a .NET Standard project that referenced a different version of data annotations.
Adding the ReadAsAsync line below highlighted the cause for me:
public async Task<HttpResponseMessage> Register(RegistrationDetails registrationDetails)
{
var regDetails = await Request.Content.ReadAsAsync<RegistrationDetails>();
If this is because Web API 2 ran into a deserialization problem due to mismatched data types, it's possible to find out where it failed by inspecting the content stream. It will read up until it hits an error, so if you read the content as a string, you should have the back half of the data you posted:
string json = await Request.Content.ReadAsStringAsync();
Fix that parameter, and it should make it further next time (or succeed if you're lucky!)...
In my case, using postman I was sending a DateTime with invalid separators (%) so the parse failed silently. Be sure you are passing valid params to your class constructor.
참고URL : https://stackoverflow.com/questions/22741943/web-api-post-body-object-always-null
'IT Share you' 카테고리의 다른 글
phpmyadmin을 사용하여 저장 프로 시저를 작성하는 방법과 PHP를 통해 사용하는 방법은 무엇입니까? (0) | 2020.12.02 |
---|---|
문자열에서 특정 문자 발생 횟수 계산 (0) | 2020.12.02 |
C ++-십진수를 이진으로 변환 (0) | 2020.12.02 |
삽입시 Postgres 오류-오류 : "UTF8"인코딩에 대한 잘못된 바이트 시퀀스 : 0x00 (0) | 2020.12.01 |
segfault 커널 로그 메시지를 읽는 방법 (0) | 2020.12.01 |