Làm cách nào để đăng JSON lên máy chủ bằng C #?


269

Đây là mã tôi đang sử dụng:

// create a request
HttpWebRequest request = (HttpWebRequest)
WebRequest.Create(url); request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
request.Method = "POST";


// turn our request string into a byte stream
byte[] postBytes = Encoding.UTF8.GetBytes(json);

// this is important - make sure you specify type this way
request.ContentType = "application/json; charset=UTF-8";
request.Accept = "application/json";
request.ContentLength = postBytes.Length;
request.CookieContainer = Cookies;
request.UserAgent = currentUserAgent;
Stream requestStream = request.GetRequestStream();

// now send it
requestStream.Write(postBytes, 0, postBytes.Length);
requestStream.Close();

// grab te response and print it out to the console along with the status code
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string result;
using (StreamReader rdr = new StreamReader(response.GetResponseStream()))
{
    result = rdr.ReadToEnd();
}

return result;

Khi tôi chạy cái này, tôi luôn gặp lỗi 500 máy chủ nội bộ.

Tôi đang làm gì sai?


1
Đầu tiên, hãy chắc chắn rằng dữ liệu bạn đăng là những gì máy chủ mong đợi.
LB

thực ra, có vẻ như tôi đã đăng dữ liệu không hợp lệ ...
Arsen Zahray

Để dễ dàng cho công việc, bạn cũng có thể thêm thư viện json vào phòng thu trực quan của mình
Alireza Tabatabaeian

@Arsen - Máy chủ không bị sập với dữ liệu không đúng định dạng. Nộp báo cáo lỗi.
jww

Câu trả lời:


396

Cách tôi làm và đang làm việc là:

var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://url");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";

using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
    string json = "{\"user\":\"test\"," +
                  "\"password\":\"bla\"}";

    streamWriter.Write(json);
}

var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
    var result = streamReader.ReadToEnd();
}

Tôi đã viết một thư viện để thực hiện nhiệm vụ này một cách đơn giản hơn, nó ở đây: https://github.com/ademargomes/JsonRequest

Hy vọng nó giúp.


3
Tôi nghĩ dòng chuỗi json phải là: chuỗi json = "{\" user \ ": \" test \ "," + "\" password \ ": \" bla \ "}"; Có vẻ như bạn đang thiếu một \
Dream Lane

3
Luôn luôn sử dụng "application / json" (trừ khi vì một số lý do khác văn bản / json là cần thiết, ví dụ: entwicklungsgedanken.de/2008/06/06/ trên ). Crowing đi tới: stackoverflow.com/questions/477816/ .
Yaniv

34
Tôi đã nghĩ rằng streamWriter.Flush (); và streamWriter.C Đóng (); là không cần thiết vì bạn đang ở trong một khối sử dụng. Khi kết thúc khối sử dụng, trình ghi luồng sẽ đóng lại.
Ruchira

1
Đừng xây dựng JSON theo cách thủ công. Rất dễ mắc lỗi cho phép tiêm JSON.
Mùa đông

5
@ user3772108 Xem stackoverflow.com/a/16380064/2279059 . Sử dụng thư viện JSON, chẳng hạn như Newtonsoft JSON.Net và kết xuất chuỗi JSON từ một đối tượng hoặc sử dụng tuần tự hóa. Tôi hiểu rằng điều này đã bị bỏ qua ở đây vì đơn giản (mặc dù mức tăng đơn giản là tối thiểu), nhưng định dạng chuỗi dữ liệu có cấu trúc (JSON, XML, ...) quá nguy hiểm để thực hiện ngay cả trong các tình huống tầm thường và khuyến khích mọi người sao chép mã đó .
Mùa đông

149

Giải pháp Ademar có thể được cải thiện bằng cách tận dụng JavaScriptSerializer's Serializephương pháp để cung cấp chuyển đổi ngầm của đối tượng để JSON.

Ngoài ra, có thể tận dụng usingchức năng mặc định của câu lệnh để bỏ qua việc gọi FlushClose.

var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://url");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";

using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
    string json = new JavaScriptSerializer().Serialize(new
                {
                    user = "Foo",
                    password = "Baz"
                });

    streamWriter.Write(json);
}

var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
    var result = streamReader.ReadToEnd();
}

1
Có gì khác biệt giữa mã này và mã trên, tôi có thiếu thứ gì không?
JMK

16
Điều này sử dụng phương thức Nối tiếp của JavaScriptSerializer để tạo JSON hợp lệ thay vì tự tạo thủ công.
Sean Anderson

Xem câu trả lời của Jean F bên dưới - nên là một bình luận. Hãy cẩn thận với các loại nội dung application/jsonlà chính xác.
Lucas

@SeanAnderson Tôi liên tục gặp lỗi "Không thể kết nối với máy chủ từ xa".
ralphgabb

3
@LuzanBaral bạn chỉ cần một hội đồng: System.Web.Extensions
Norbrecht

60

Các HttpClientloại là một thực hiện mới hơn WebClientHttpWebRequest.

Bạn chỉ có thể sử dụng các dòng sau.

string myJson = "{'Username': 'myusername','Password':'pass'}";
using (var client = new HttpClient())
{
    var response = await client.PostAsync(
        "http://yourUrl", 
         new StringContent(myJson, Encoding.UTF8, "application/json"));
}

nhập mô tả hình ảnh ở đây

Khi bạn cần HttpClientnhiều hơn một lần, bạn chỉ nên tạo một phiên bản và sử dụng lại hoặc sử dụng cái mới HttpClientFactory.


5
Một lưu ý nhỏ trên HttpClient, sự đồng thuận chung là bạn không nên loại bỏ nó. Thậm chí, nó còn thực hiện IDis Dùng một lần đối tượng là Thread-Safe và có nghĩa là được sử dụng lại. stackoverflow.com/questions/15705092/ từ
Jean F.

1
@JeanF. Hey Cảm ơn cho đầu vào. Như tôi đã lưu ý, bạn chỉ nên tạo một thể hiện hoặc sử dụng HttpClientFactory. Tôi đã không đọc tất cả các câu trả lời trong vấn đề được liên kết nhưng tôi nghĩ rằng nó cần cập nhật vì nó không đề cập đến nhà máy.
NtFreX

33

Hơn nữa với bài viết của Sean, không cần thiết phải lồng các câu lệnh sử dụng. Bởi usingStreamWriter, nó sẽ được xóa và đóng ở cuối khối nên không cần gọi rõ ràng các phương thức Flush()Close():

var request = (HttpWebRequest)WebRequest.Create("http://url");
request.ContentType = "application/json";
request.Method = "POST";

using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
    string json = new JavaScriptSerializer().Serialize(new
                {
                    user = "Foo",
                    password = "Baz"
                });

    streamWriter.Write(json);
}

var response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
        var result = streamReader.ReadToEnd();
}

1
bây giờ câu trả lời này và câu trả lời của Sean Anderson hoàn toàn giống nhau, vì Sean đã chỉnh sửa bài đăng của mình.
faza

Này, điều này thật tuyệt vời. Cảm ơn. Nhưng làm thế nào chúng ta sẽ truyền dữ liệu nếu chúng ta có các nút con trên json của chúng ta?
dùng2728409

1
Trình tuần tự hóa có thể xử lý các nút con trong json - bạn chỉ cần cung cấp cho nó một đối tượng json hợp lệ.
David Clarke

14

Nếu bạn cần gọi không đồng bộ thì hãy sử dụng

var request = HttpWebRequest.Create("http://www.maplegraphservices.com/tokkri/webservices/updateProfile.php?oldEmailID=" + App.currentUser.email) as HttpWebRequest;
            request.Method = "POST";
            request.ContentType = "text/json";
            request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);

private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
    {
        HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
        // End the stream request operation

        Stream postStream = request.EndGetRequestStream(asynchronousResult);


        // Create the post data
        string postData = JsonConvert.SerializeObject(edit).ToString();

        byte[] byteArray = Encoding.UTF8.GetBytes(postData);


        postStream.Write(byteArray, 0, byteArray.Length);
        postStream.Close();

        //Start the web request
        request.BeginGetResponse(new AsyncCallback(GetResponceStreamCallback), request);
    }

    void GetResponceStreamCallback(IAsyncResult callbackResult)
    {
        HttpWebRequest request = (HttpWebRequest)callbackResult.AsyncState;
        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callbackResult);
        using (StreamReader httpWebStreamReader = new StreamReader(response.GetResponseStream()))
        {
            string result = httpWebStreamReader.ReadToEnd();
            stat.Text = result;
        }

    }

3
Cảm ơn bạn đã đăng giải pháp này Vivek. Trong kịch bản của chúng tôi, chúng tôi đã thử một giải pháp khác trong bài đăng này và thấy các ngoại lệ của System.Threading trong ứng dụng của chúng tôi, do những gì tôi cho là các bài viết chặn các bài viết đồng bộ. Mã của bạn đã giải quyết vấn đề của chúng tôi.
Ken Palmer

Lưu ý rằng bạn có thể không phải chuyển đổi thành byte. Bạn sẽ có thể làm postStream.Write(postData);- và tùy thuộc vào API, có thể phải sử dụng request.ContentType = "application/json";thay thế text/json.
vapcguy


11

Gần đây tôi đã nghĩ ra một cách đơn giản hơn nhiều để đăng JSON, với bước chuyển đổi bổ sung từ một mô hình trong ứng dụng của tôi. Lưu ý rằng bạn phải tạo mô hình [JsonObject] cho bộ điều khiển của bạn để nhận các giá trị và thực hiện chuyển đổi.

Yêu cầu:

 var model = new MyModel(); 

 using (var client = new HttpClient())
 {
     var uri = new Uri("XXXXXXXXX"); 
     var json = new JavaScriptSerializer().Serialize(model);
     var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
     var response = await Client.PutAsync(uri,stringContent).Result;
     ...
     ...
  }

Mô hình:

[JsonObject]
[Serializable]
public class MyModel
{
    public Decimal Value { get; set; }
    public string Project { get; set; }
    public string FilePath { get; set; }
    public string FileName { get; set; }
}

Phía máy chủ:

[HttpPut]     
public async Task<HttpResponseMessage> PutApi([FromBody]MyModel model)
{
    ...
    ... 
}

6

Tùy chọn này không được đề cập:

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("http://localhost:9000/");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var foo = new User
    {
        user = "Foo",
        password = "Baz"
    }

    await client.PostAsJsonAsync("users/add", foo);
}

2
Tùy chọn này không còn khả dụng kể từ .Net 4.5.2. xem tại đây stackoverflow.com/a/40525794/2161568
Downhillski

Downvote theo nhận xét trên - vì điều này không có sẵn, có lẽ nên xóa câu trả lời.
NovaDev

1
Đó không phải là một lý do chính đáng để hạ thấp câu trả lời này vì không phải ai cũng sử dụng các phiên bản mới nhất của .net và do đó đây là một câu trả lời hợp lệ.
Ellisan

4

Một số cách khác nhau và rõ ràng để đạt được điều này là bằng cách sử dụng HttpClient như thế này:

public async Task<HttpResponseMessage> PostResult(string url, ResultObject resultObject)
{
    using (var client = new HttpClient())
    {
        HttpResponseMessage response = new HttpResponseMessage();
        try
        {
            response = await client.PostAsJsonAsync(url, resultObject);
        }
        catch (Exception ex)
        {
            throw ex
        }
        return response;
     }
}

4
Hữu ích, tuy nhiên PostAsJsonAsynckhông còn có sẵn kể từ .NET 4.5.2. Sử dụng PostAsyncthay thế. Thêm tại đây
Zachary Keener

Nói chung, httpClient không nên được sử dụng trong một usingtuyên bố như thế này
p3tch

Tôi nghĩ rằng nó thực hiện IDisposablegiao diện vì một lý do
Dima Daron

4

CẢNH BÁO! Tôi có một cái nhìn rất mạnh mẽ về chủ đề này.

Các máy khách web hiện tại của .NET không thân thiện với nhà phát triển! WebRequest & WebClient là những ví dụ điển hình về "cách làm nản lòng nhà phát triển". Họ dài dòng và phức tạp để làm việc với; khi tất cả những gì bạn muốn làm là một yêu cầu Đăng đơn giản trong C #. HttpClient đi một số cách để giải quyết các vấn đề này, nhưng nó vẫn còn thiếu. Trên hết tài liệu của Microsoft là xấu thật sự xấu; trừ khi bạn muốn chọn lọc thông qua các trang và trang kỹ thuật.

Nguồn mở để giải cứu. Có ba thư viện NuGet miễn phí, mã nguồn mở tuyệt vời thay thế. Ơn Chúa! Tất cả đều được hỗ trợ tốt, được ghi lại và có, rất dễ điều chỉnh, siêu dễ dàng để làm việc với.

  • ServiceStack.Text - nhanh, nhẹ và đàn hồi.
  • RestSharp - Máy khách API REST và HTTP đơn giản
  • Flurl - một thư viện máy khách HTTP thông thạo, di động, có thể kiểm tra

Không có nhiều thứ giữa họ, nhưng tôi sẽ cung cấp cho ServiceStack. Tiếp theo là cạnh nhẹ

  • Các ngôi sao Github gần giống nhau.
  • Các vấn đề mở & quan trọng là làm thế nào nhanh chóng bất kỳ vấn đề đóng cửa? ServiceStack nhận giải thưởng ở đây để giải quyết vấn đề nhanh nhất & không có vấn đề mở.
  • Tài liệu? Tất cả đều có tài liệu tuyệt vời; tuy nhiên, ServiceStack đưa nó lên cấp độ tiếp theo và được biết đến với 'Tiêu chuẩn vàng' cho tài liệu.

Ok - vậy một Yêu cầu bài viết trong JSON trông như thế nào trong ServiceStack.Text?

var response = "http://example.org/login"
    .PostJsonToUrl(new Login { Username="admin", Password="mypassword" });

Đó là một dòng mã. Súc tích & dễ dàng! So sánh ở trên với các thư viện http của .NET.


3

Cuối cùng tôi đã gọi trong chế độ đồng bộ hóa bằng cách bao gồm .Result

HttpResponseMessage response = null;
try
{
    using (var client = new HttpClient())
    {
       response = client.PostAsync(
        "http://localhost:8000/....",
         new StringContent(myJson,Encoding.UTF8,"application/json")).Result;
    if (response.IsSuccessStatusCode)
        {
            MessageBox.Show("OK");              
        }
        else
        {
            MessageBox.Show("NOK");
        }
    }
}
catch (Exception ex)
{
    MessageBox.Show("ERROR");
}

1

var data = Encoding.ASCII.GetBytes(json);

byte[] postBytes = Encoding.UTF8.GetBytes(json);

Sử dụng ASCII thay vì UFT8


2
Nghe có vẻ là một ý tưởng khá tồi tệ, tôi đang thiếu một cái gì đó?
CyberFox

JSON có thể chứa các ký tự UTF8, đây có vẻ là một ý tưởng khủng khiếp.
Adrian Smith
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.