System.Net.HttpClient를 사용하여 복잡한 형식을 게시하는 방법은 무엇입니까?


102

Web API를 사용하여 작업하려는 사용자 지정 복합 유형이 있습니다.

public class Widget
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

그리고 여기 내 웹 API 컨트롤러 방법이 있습니다. 이 개체를 다음과 같이 게시하고 싶습니다.

public class TestController : ApiController
{
    // POST /api/test
    public HttpResponseMessage<Widget> Post(Widget widget)
    {
        widget.ID = 1; // hardcoded for now. TODO: Save to db and return newly created ID

        var response = new HttpResponseMessage<Widget>(widget, HttpStatusCode.Created);
        response.Headers.Location = new Uri(Request.RequestUri, "/api/test/" + widget.ID.ToString());
        return response;
    }
}

이제 System.Net.HttpClient메서드를 호출하는 데 사용하고 싶습니다 . 그러나 PostAsync메서드 에 전달할 개체 유형 과 구성 방법을 잘 모르겠습니다 . 다음은 샘플 클라이언트 코드입니다.

var client = new HttpClient();
HttpContent content = new StringContent("???"); // how do I construct the Widget to post?
client.PostAsync("http://localhost:44268/api/test", content).ContinueWith(
    (postTask) =>
    {
        postTask.Result.EnsureSuccessStatusCode();
    });

HttpContent웹 API가 이해하는 방식으로 객체를 생성하려면 어떻게해야 합니까?


XML 직렬화 된 버전의 객체를 서비스 엔드 포인트에 제출해 보셨습니까?
Joshua Drake

답변:


132

원본 HttpRequestMessage<T>제거되었습니다 . 이 :

new HttpRequestMessage<Widget>(widget)

이상 작동 전혀 없이도 .

대신 이 게시물 에서 ASP.NET 팀은 이 기능을 지원하기위한 몇 가지 새로운 호출 을 포함했습니다 .

HttpClient.PostAsJsonAsync<T>(T value) sends application/json
HttpClient.PostAsXmlAsync<T>(T value) sends application/xml

따라서 새 코드 ( from dunston )는 다음과 같습니다.

Widget widget = new Widget()
widget.Name = "test"
widget.Price = 1;

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:44268");
client.PostAsJsonAsync("api/test", widget)
    .ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode() );

1
예,하지만 Widget 클래스에 대한 액세스 권한이 없으면 어떻게됩니까?
contactmatt

13
새로운 HttpClient.PostAsXXXAsync<T>( T value ) methods are great, but what about one for application/x-www-form-urlencoded format? Is there a simple / short way for that or do we still need to create elaborate KeyValuePair의 목록은 무엇입니까?
Jaans

1
@Jaans Flurl.HttpPostUrlEncodedAsync.
Todd Menier

16
참고가 사용할 수 있도록 System.Net.Http.Formatting에 대한 참조를 추가 할 필요가 PostAsJsonAsync또는PostAsXmlAsync
피트

6
PostAsJsonAcync를 사용하려면 NuGet 패키지 Microsoft.AspNet.WebApi.Client !!
Dennis

99

SendAsync대신 메소드를 사용해야합니다 . 이것은 서비스에 대한 입력을 직렬화하는 일반 메소드입니다.

Widget widget = new Widget()
widget.Name = "test"
widget.Price = 1;

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:44268/api/test");
client.SendAsync(new HttpRequestMessage<Widget>(widget))
    .ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode() );

당신이 구체적인 클래스를 생성하지 않으려면, 당신은 그것을 할 수 FormUrlEncodedContent클래스

var client = new HttpClient();

// This is the postdata
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("Name", "test"));
postData.Add(new KeyValuePair<string, string>("Price ", "100"));

HttpContent content = new FormUrlEncodedContent(postData); 

client.PostAsync("http://localhost:44268/api/test", content).ContinueWith(
    (postTask) =>
    {
        postTask.Result.EnsureSuccessStatusCode();
    });

참고 : ID를 nullable int ( int?) 로 만들어야합니다.


1
이것은 Widget 개체를 포함하는 어셈블리에 대한 참조가없는 외부 프로젝트에서 호출됩니다. 올바른 속성을 포함하는 익명 형식의 개체를 만들고이 메서드를 사용하여 직렬화하고 그런 방식으로 전달하려고 시도했지만 500 내부 서버 오류가 발생합니다. 웹 API 컨트롤러 메서드에 도달하지 않습니다.
indot_brad 2012

아-그러면 webapi 서비스에 xml 또는 json을 게시해야합니다. 그러면 역 직렬화됩니다. 동일한 작업을 수행합니다. SendAsync는 서비스에 대한 개체를 직렬화합니다
dunston

1
방금 업데이트했습니다-코드를
테스트

8
"일반 형식이 아닌 'System.Net.Http.HttpRequestMessage'는 형식 인수와 함께 사용할 수 없습니다."라는 메시지가 나타납니다. 여전히 유효합니까?
user10479

5
그래 첫 번째 솔루션은 더 이상 작동하지 않습니다. aspnetwebstack.codeplex.com/discussions/350492
Giovanni B

74

이식 가능한 클래스 라이브러리를 사용하는 경우 HttpClient에는 PostAsJsonAsync 메서드가 없습니다 . 이식 가능한 클래스 라이브러리를 사용하여 콘텐츠를 JSON으로 게시하려면 다음을 수행해야합니다.

HttpClient client = new HttpClient();
HttpContent contentPost = new StringContent(argsAsJson, Encoding.UTF8, 
"application/json");

await client.PostAsync(new Uri(wsUrl), contentPost).ContinueWith(
(postTask) => postTask.Result.EnsureSuccessStatusCode());

argsAsJson이 직렬화 된 객체에서 왔고이 객체는 속성 즉, Content = "domain \ user"이면 \가 두 번 인코딩됩니다. argsAsJson으로 직렬화 될 때 한 번, PostAsync가 contentPost를 게시 할 때 두 번째. 이중 인코딩을 피하는 방법은 무엇입니까?
Krzysztof Morcinek 2014 년

3
훌륭한 @fabiano! 이것은 정말로 트릭을했다. 이 두 가지 추가 인수는 이러한 유형의 프로젝트에 필요합니다.
Peter Klein

@PeterKlein 아주 좋아요! 웹상의 Microsoft 설명서에서이 정보를 찾을 수 없으므로 동일한 문제를 가진 다른 사람들에게 도움이 될 수 있습니다. 내 프로젝트는이 트릭 없이는 데이터를 전송하지 않습니다.
Fabiano

1
당신은 또한 요청에 "응용 프로그램 / JSON"를 추가해야 할 수도 있음을주의 당, Accept 헤더의 stackoverflow.com/a/40375351/3063273
매트 토마스

4

다른 답변에 언급 된 편의 방법의 유형을 원하지만 이식성이 필요하다면 (또는 그렇지 않더라도) Flurl [공개 : 저자입니다] 를 확인하는 것이 좋습니다 . 그것은 (얇게) 랩핑 HttpClient하고 Json.NET을 만들고 일부 구워진 테스트 도우미를 포함하여 유창한 설탕과 기타 좋은 것들을 추가 합니다 .

JSON으로 게시 :

var resp = await "http://localhost:44268/api/test".PostJsonAsync(widget);

또는 URL 인코딩 :

var resp = await "http://localhost:44268/api/test".PostUrlEncodedAsync(widget);

위의 두 예제는 모두를 반환 HttpResponseMessage하지만 Flurl에는 추적을 위해 다른 것을 반환하는 확장 메서드가 포함되어 있습니다.

T poco = await url.PostJsonAsync(data).ReceiveJson<T>();
dynamic d = await url.PostUrlEncodedAsync(data).ReceiveJson();
string s = await url.PostUrlEncodedAsync(data).ReceiveString();

Flurl은 NuGet에서 사용할 수 있습니다.

PM> Install-Package Flurl.Http

1

많은 대안을 조사한 후 API 2.0 버전에 적합한 다른 접근 방식을 발견했습니다.

(VB.NET은 내가 제일 좋아하는, sooo ...)

Public Async Function APIPut_Response(ID as Integer, MyWidget as Widget) as Task(Of HttpResponseMessage)
    Dim DesiredContent as HttpContent = New StringContent(JsonConvert.SerializeObject(MyWidget))
    Return Await APIClient.PutAsync(String.Format("api/widget/{0}", ID), DesiredContent)
End Function

행운을 빕니다! 나를 위해 이것은 (결국!) 해결되었습니다.

감사합니다, 피터


1
이것은 @Fabiano가 위에서 제시 한 제안으로 일을 발생시킵니다.
Peter Klein

2
VB.NET은 좋아하는 사람이 없습니다. :)
Lazy Coder

1

나는 당신이 이것을 할 수 있다고 생각합니다.

var client = new HttpClient();
HttpContent content = new Widget();
client.PostAsync<Widget>("http://localhost:44268/api/test", content, new FormUrlEncodedMediaTypeFormatter())
    .ContinueWith((postTask) => { postTask.Result.EnsureSuccessStatusCode(); });

1

나와 같은 사람이 위의 모든 내용을 이해하지 못한 경우 나를 위해 일하는 쉬운 예를 제공합니다. URL이 " http://somesite.com/verifyAddress "인 웹 API 가있는 경우 게시 방법이며 주소 개체를 전달해야합니다. 코드에서이 API를 호출하려고합니다. 여기에서 할 수있는 일이 있습니다.

    public Address verifyAddress(Address address)
    {
        this.client = new HttpClient();
        client.BaseAddress = new Uri("http://somesite.com/");
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        var urlParm = URL + "verifyAddress";
        response = client.PostAsJsonAsync(urlParm,address).Result;
        var dataObjects = response.IsSuccessStatusCode ? response.Content.ReadAsAsync<Address>().Result : null;
        return dataObjects;
    }

0

이것은 여기에 다른 답변을 기반으로 한 코드입니다. 이것은 복잡한 유형을 수신하고 응답하는 HttpPost를위한 것입니다.

Task<HttpResponseMessage> response = httpClient.PostAsJsonAsync(
                       strMyHttpPostURL,
                       new MyComplexObject { Param1 = param1, Param2 = param2}).ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode());
                    //debug:
                    //String s = response.Result.Content.ReadAsStringAsync().Result;
                    MyOtherComplexType moct = (MyOtherComplexType)JsonConvert.DeserializeObject(response.Result.Content.ReadAsStringAsync().Result, typeof(MyOtherComplexType));

-1

다음과 같이 서비스 호출을합니다.

public async void SaveActivationCode(ActivationCodes objAC)
{
    var client = new HttpClient();
    client.BaseAddress = new Uri(baseAddress);
    HttpResponseMessage response = await client.PutAsJsonAsync(serviceAddress + "/SaveActivationCode" + "?apiKey=445-65-1216", objAC);
} 

다음과 같은 서비스 방법 :

public HttpResponseMessage PutSaveActivationCode(ActivationCodes objAC)
{
}

PutAsJsonAsync는 네트워크를 통한 직렬화 및 역 직렬화를 처리합니다.


요청 된 POST가 아닌 HTTP PUT 메시지를 보냅니다. 다른 사람들이 말했듯 PostAsJsonAsync이 JSON의 POST로 require 데이터를 보냅니다.
Zhaph-Ben Duguid
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.