Как отправить JSON на сервер с помощью C #?

269

Вот код, который я использую:

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

Когда я запускаю это, я всегда получаю 500 внутренних ошибок сервера.

Что я делаю не так?

Арсен Захрай
источник
1
Во-первых, убедитесь, что данные, которые вы публикуете, соответствуют ожиданиям сервера.
LB
на самом деле, похоже, что я публиковал неверные данные ...
Арсен Захрай
Для простоты работы вы также можете добавить библиотеку json в свою визуальную студию
Alireza Tabatabaeian
@Arsen - Сервер не должен аварийно завершать работу с искаженными данными. Подайте отчет об ошибке.
августа

Ответы:

396

Я делаю это и работаю так:

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

Я написал библиотеку для более простого выполнения этой задачи, она находится здесь: https://github.com/ademargomes/JsonRequest

Надеюсь, поможет.

Адемар
источник
3
Я думаю, что строка строки json должна быть: string json = "{\" user \ ": \" test \ "," + "\" password \ ": \" bla \ "}"; Похоже, вы пропустили \
Dream Lane
3
Всегда используйте «application / json» (если по какой-то другой причине не требуется текст / json, например: entwicklungsgedanken.de/2008/06/06/… ). Кредитование отправляется на: stackoverflow.com/questions/477816/… .
Янив
34
Я бы подумал, что streamWriter.Flush (); и streamWriter.Close (); не обязательно, так как вы находитесь внутри блока использования. По окончании использования блока потоковая запись все равно закроется.
Ruchira
1
Не создавайте JSON вручную. Легко делать ошибки, которые допускают внедрение JSON.
Флориан Зима
5
@ user3772108 См. stackoverflow.com/a/16380064/2279059 . Используйте библиотеку JSON, такую ​​как Newtonsoft JSON.Net, и визуализируйте строку JSON из объекта или используйте сериализацию. Я понимаю, что это было опущено здесь для простоты (хотя выигрыш в простоте минимален), но форматирование структурированных строк данных (JSON, XML, ...) слишком опасно, чтобы делать это даже в тривиальных сценариях и побуждать людей копировать такой код ,
Флориан Зима
149

Решение Адемар может быть улучшена за счет использования JavaScriptSerializer«с Serializeметодом , чтобы обеспечить неявное преобразование объекта в JSON.

Кроме того, можно использовать функциональные возможности usingоператора по умолчанию для явного исключения вызова Flushи Close.

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
В чем разница между этим и приведенным выше кодом, я что-то упустил?
JMK
16
При этом используется метод Serialize в JavaScriptSerializer для создания действительного JSON, а не его ручная обработка.
Шон Андерсон
Смотрите ответ Жана Ф. ниже - должен быть комментарий. Будьте осторожны с типом контента application/jsonправильно.
Лукас
@SeanAnderson У меня постоянно возникает ошибка «Невозможно подключиться к удаленному серверу».
ralphgabb
3
@LuzanBaral вам просто нужна сборка: System.Web.Extensions
Norbrecht
60

HttpClientТип новая реализация , чем WebClientи HttpWebRequest.

Вы можете просто использовать следующие строки.

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

введите описание изображения здесь

Если вам нужно HttpClientнесколько раз, рекомендуется создать только один экземпляр и использовать его повторно или использовать новый HttpClientFactory.

NtFreX
источник
5
Небольшое замечание по HttpClient, общее мнение заключается в том, что вы не должны распоряжаться им. Даже он реализует IDisposable, объект является Thread-Safe и предназначен для повторного использования. stackoverflow.com/questions/15705092/…
Жан Ф.
1
@JeanF. Эй, спасибо за вклад. Как я уже отметил, вы должны создать только один экземпляр или использовать HttpClientFactory. Я не прочитал все ответы в связанной проблеме, но я думаю, что это нуждается в обновлении, поскольку это не упоминает фабрику.
NtFreX
33

В дополнение к посту Шона, нет необходимости вкладывать операторы использования. К usingв StreamWriter она будет очищена и закрыта в конце блока, нет необходимости явно вызывать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
теперь этот ответ и ответ Шона Андерсона точно такие же, как Шон редактировал свой пост.
Фаза
Эй, это так здорово. Спасибо. Но как мы будем передавать данные, если у нас есть дочерние узлы на нашем json?
user2728409
1
Сериализатор может обрабатывать дочерние узлы в json - вы просто должны предоставить ему действительный объект json.
Дэвид Кларк
14

Если вам нужно вызвать асинхронно, используйте

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
Спасибо за размещение этого решения Vivek. В нашем сценарии мы попробовали другое решение в этом посте и в итоге увидели System.Threading исключения в нашем приложении, из-за того, что я предполагаю, что синхронные посты блокировали потоки. Ваш код решил нашу проблему.
Кен Палмер
Обратите внимание, что вам, вероятно, не нужно конвертировать в байты. Вы должны быть в состоянии сделать postStream.Write(postData);- и в зависимости от API, возможно, придется использовать request.ContentType = "application/json";вместо text/json.
vapcguy
11

Недавно я предложил гораздо более простой способ публикации JSON с дополнительным этапом преобразования из модели в моем приложении. Обратите внимание, что вы должны сделать модель [JsonObject] для вашего контроллера, чтобы получить значения и выполнить преобразование.

Запрос:

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

Модель:

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

Сторона сервера:

[HttpPut]     
public async Task<HttpResponseMessage> PutApi([FromBody]MyModel model)
{
    ...
    ... 
}
Dustin
источник
6

Эта опция не упоминается:

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);
}
Centro
источник
2
Эта опция больше не доступна, так как .Net 4.5.2. смотрите здесь stackoverflow.com/a/40525794/2161568
Downhillski
Downvote за вышеупомянутый комментарий - так как это не доступно, вероятно, следует удалить ответ.
NovaDev
1
Это не веская причина для отрицательного ответа на этот ответ, так как не все используют последние версии .net, и, следовательно, это правильный ответ.
Эллисан
4

Некоторым другим и чистым способом достижения этого является использование HttpClient следующим образом:

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
Полезно, однако PostAsJsonAsyncбольше не доступно, так как .NET 4.5.2. Используйте PostAsyncвместо этого. Подробнее здесь
Захари Кинер
HttpClient, как правило, не должен использоваться в таком usingутверждении
p3tch
Я думаю, что это реализует IDisposableинтерфейс по причине
Дима Дарон
4

ПРЕДУПРЕЖДЕНИЕ! У меня очень сильный взгляд на эту тему.

Существующие веб-клиенты .NET не подходят для разработчиков! WebRequest и WebClient являются яркими примерами того, «как расстроить разработчика». Они многословны и сложны в работе; когда все, что вы хотите сделать, это простой запрос Post в C #. HttpClient помогает решить эти проблемы, но все равно не дотягивает . Кроме того, документация Microsoft плохая… действительно плохая; если вы не хотите просеивать страницы и страницы технической рекламы.

Открытый исходный код на помощь. В качестве альтернативы есть три отличных бесплатных библиотеки NuGet с открытым исходным кодом. Слава Богу! Все они хорошо поддерживаются, документируются и, да, легко - исправляются ... очень легко - работать с ними.

  • ServiceStack.Text - быстро, легко и устойчиво.
  • RestSharp - простой REST и HTTP API клиент
  • Flurl - , портативная, тестируемая клиентская библиотека HTTP

Между ними не так много, но я бы дал ServiceStack.Text небольшое преимущество ...

  • Github звезды примерно одинаковы.
  • Открытые проблемы и, главное, как быстро все проблемы закрылись?ServiceStack получает здесь награду за самое быстрое решение проблем и отсутствие открытых проблем.
  • Документация?У всех есть отличная документация; однако ServiceStack выводит его на новый уровень и известен своим «золотым стандартом» для документации.

Итак, как же выглядит Post Post в JSON внутри ServiceStack.Text?

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

Это одна строка кода. Краткий и легкий! Сравните приведенное выше с библиотеками .NET Http.

Программирование с Марком
источник
3

Я наконец вызвал в режиме синхронизации, включив .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");
}
lgturrez
источник
1

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

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

Используйте ASCII вместо UFT8

user3280472
источник
2
звучит как довольно плохая идея, я что-то упустил?
CyberFox
JSON может содержать символы UTF8, это кажется ужасной идеей.
Адриан Смит