json POST 데이터를 웹 API 메소드에 객체로 전달하는 방법은 무엇입니까?


304

ASP.NET MVC4 웹 API 응용 프로그램은 고객을 저장하기 위해 사후 방법을 정의합니다. 고객은 POST 요청 본문에서 json 형식으로 전달됩니다. 사후 메소드의 고객 매개 변수에 특성의 널값이 포함되어 있습니다.

게시 된 데이터가 고객 객체로 전달되도록이 문제를 해결하는 방법은 무엇입니까?

가능한 경우 Content-Type : application / x-www-form-urlencoded를 사용해야합니다. 폼을 게시하는 javascript 메소드에서 변경하는 방법을 모르기 때문에 사용해야합니다.

제어 장치:

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"}

답변:


525

편집 : 2017 년 10 월 31 일

동일한 코드 / 접근 방식이 Asp.Net Core 2.0 에서도 작동 합니다. 가장 큰 차이점은 asp.net 코어에서 웹 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 월 편집

복잡한 객체 게시

다음과 같이 웹 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에서는 웹 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 :)
});

모델 바인딩은 일부 속성에는 작동하지만 전부는 아닙니다! 왜 ?

웹 API 메소드 매개 변수를 [FromBody]속성으로 장식하지 않은 경우

[HttpPost]
public CreateUserViewModel Save(CreateUserViewModel m)
{
    return m;
}

그리고 contentType 속성 값을 지정하지 않고 모델 (JSON 형식이 아닌 원시 자바 스크립트 객체)을 보냅니다.

$.ajax({
    type: "POST",
    data: model,
    url: "../product/save"
}).done(function (res) {
     console.log('res', res);
});

모델 바인딩은 유형이 복잡한 / 다른 유형의 속성이 아니라 모델의 플랫 속성에 적용됩니다. 우리의 경우, IdName특성이 제대로 매개 변수에 바인딩 될 것이다 m, 그러나 Tags속성은 빈 목록이 될 것입니다.

짧은 버전을 사용하는 경우 동일한 문제가 발생 $.post하며 요청을 보낼 때 기본 Content-Type을 사용합니다.

$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});

4
내가 한 일을 확신하지 못했지만 오늘 아침에 같은 보트로 돌아 왔습니다. 컨트롤러에서 개체가 null입니다. 여기에 우리는 다시 간다
Grayson

1
피들러를 사용하는 동안 컨텐츠 유형이 "Content-Type : application / json"으로 작성되어 있는지 확인하십시오. 건배!
ioWint

1
당신은 단순히 하루의 일을 해결했습니다 !!! 이 작은 함수 "JSON.stringify (data)"가 만들었습니다!
Gil Allen

1
이 작업을 수행하고 (Content-Type 헤더 변경) CORS 요청을하는 경우 jQuery는 POST 전에 서버가 처리해야하는 프리 플라이트 OPTIONS 요청을 추가하기 시작합니다.
Arbiter 2016 년

1
복잡한 유형의 문제로 인해 'contentType :'application / json; '을 지정하는 습관이 있다고 생각합니다. json은 js 객체를 문자열 화 한 다음 [FromBody] 속성을 사용할 필요가 없습니다.
BornToCode

69

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가 나타납니다.

셋째, 우선 단일 작업으로 여러 SIMPLE 유형을 게시 할 수 없습니다. 또한 쿼리 문자열이나 REST 스타일로 매개 변수를 전달하는 것 외에도 단일 단순 유형 을 게시 하는 특수 형식 이 있습니다. 이것은 Fiddler 및 Chrome의 고급 REST 클라이언트 확장과 같은 Rest Client로 머리를 두드리고 거의 5 시간 동안 웹을 사냥하면서 결국 다음 URL이 도움이되는 시점이었습니다. 링크의 관련 내용이 인용 될 수 있습니다.

Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"na@Turbo.Tina"}

추신 : 주목 독특한 구문을 ?

http://forums.asp.net/t/1883467.aspx?The+received+value+is+null+when+I+try+to+Post+to+my+Web+Api

어쨌든, 그 이야기를 극복합시다. 계속 진행 :

넷째, 복잡한 유형 게시 jQuery를 통해 을 물론 $ .ajax ()가 즉시 역할을 수행하게됩니다.

액션 메소드가 아이디와 이름을 가진 Person 객체를 받아 들인다 고하자. 따라서 자바 스크립트에서 :

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;
}

위의 모든 것이 나를 위해 일했습니다!! 건배!


2
나는 몇 달마다이 문제를 겪는 것처럼 보였으며, 대부분은 결국 그것을 해결했지만 이번에는 포기했습니다. 위의 팁 중 어느 것도 나를 위해 해결하지 못했기 때문에 이것을 접근 방식으로 비하기로 결정했습니다. 제대로하기가 어렵다면 왜 귀찮게합니까? 어쨌든 편리합니다. 컨텐츠를 문자열로 가져 와서 newtonsoft를 사용하여 변환하십시오. 끝난. "쉬운"방법으로 해결하기 위해 약 1 시간 동안 시도한 후 "하드"방법으로 해결하는 데 30 초가 걸렸습니다. 나는 접근 방식에 열광적이지 않지만 근본적인 문제가 있습니까?
Kinetic

추신 : WebApi2에서는 이제 경로 데코레이터를 사용할 수 있습니다. 따라서이 문제는 주로 해결됩니다. asp.net/web-api/overview/web-api-routing-and-actions/…
Vaibhav

2
관측치를 추가하고 싶습니다. 복잡한 유형 (예 : DTO)을 전달할 때 WebAPI 측에서 모델 바인딩이 실패 (널)되는 이유는 모델의 하나 이상의 특성이 호환되지 않거나 구문 분석에 실패하기 때문입니다. 예 : 유효하지 않은 GUID가 지정된 Guid 특성 이 경우 모든 객체 속성에 기본값 / 빈 값을 사용해보고 다시 시도하십시오.
Vaibhav

10

방금 이것으로 놀고 있었고 다소 이상한 결과를 발견했습니다. 다음과 같이 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

나는 여기에서 무슨 일이 일어나고 있는지 잘 모르겠지만 프레임 워크의 버그 (기능?)처럼 보입니다. 아마도 다른 바인딩 방법은 다른 "어댑터"를 호출하고 있으며 응용 프로그램 / json 용 어댑터는 공용 속성과 작동하지만 양식 인코딩 된 데이터 용 어댑터는 작동하지 않습니다.

그래도 모범 사례로 간주 될 생각은 없습니다.


6
속성과 필드가 다른 이유입니다. 속성이 가장 좋습니다. 첫 번째 예에서 속성이라고 부르는 것은 실제로 필드입니다. 가져 오기 / 설정을하면 자동으로 생성 된 백업 필드가있어 속성이됩니다.
paqogomez

이것은 사실이며 이상합니다. 필드 만있는 일반 클래스는 바인딩되어 게시물을 형성하지 않지만 속성은 적용됩니다. BTW : 왜 이런 경우인지 설명하지 않습니다 ...? 내부 논리가 JSON 데이터를 필드에 바인딩하고 포스트 데이터를 속성에 형성하는 것만 추측 할 수 있습니다.
James Wilkins

1
코드가 속성 만 검색하기 때문입니다. 공용 필드를 사용하는 것이 가장 좋은 방법 은 아니기 때문에 MS 팀은 가장 좋은 방법은 IMHO와 같은 좋은 방법 을 허용 하지 않기로 결정했습니다 .
Erik Philips

1

JSON 형식으로 문자열을 가져 오려면 JSON.stringify () 를 사용하십시오. AJAX 호출을 수행하는 동안 언급 된 속성 아래에 전달해야합니다.

  • contentType : 'application / json'

다음은 asp.net 웹 API에 ajax 포스트 호출을 수행하는 jquery 코드 제공입니다.

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);
    }
});


2
dataType은 필요하지 않습니다.
Erik Philips

0

WebAPI 서비스가 전달중인 JSON과 일치하는 구조를 가진 강력한 유형의 객체를 기대하고 있는지 확인하십시오. 그리고 POST중인 JSON을 문자열로 묶어야합니다.

AngluarJS를 사용하는 JavaScript는 다음과 같습니다.

$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";
}

0

다음 코드는 xml -Web API 2 대신 json 형식으로 데이터를 반환합니다.

Global.asax 파일에 다음 줄을 넣습니다.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

0
@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")
}


0

1) 클라이언트 측에서 다음과 같은 문자열로 http.post 요청을 보낼 수 있습니다

var IndexInfo = JSON.stringify(this.scope.IndexTree);
this.$http.post('../../../api/EvaluationProcess/InsertEvaluationProcessInputType', "'" + IndexInfo + "'" ).then((response: any) => {}

2) 그런 다음 웹 API 컨트롤러에서 직렬화 해제 할 수 있습니다

public ApiResponce InsertEvaluationProcessInputType([FromBody]string IndexInfo)
    {
var des = (ApiReceivedListOfObjects<TempDistributedIndex>)Newtonsoft.Json.JsonConvert.DeserializeObject(DecryptedProcessInfo, typeof(ApiReceivedListOfObjects<TempDistributedIndex>));}

3) ApiReceivedListOfObjects 클래스는 다음과 같아야합니다

public class ApiReceivedListOfObjects<T>
    {
        public List<T> element { get; set; }

    }

4) 직렬화 된 문자열 (여기에서 IndexInfo)이 2 단계에서 JsonConvert.DeserializeObject 명령 전에 구조와 같은지 확인하십시오.

var resp = @"
    {
        ""element"": [
        {
            ""A"": ""A Jones"",
            ""B"": ""500015763""
        },
        {
            ""A"": ""B Smith"",
            ""B"": ""504986213""
        },
        {
            ""A"": ""C Brown"",
            ""B"": ""509034361""
        }
        ]
    }";
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.