I had below code to call a POST method with json body, but I getting The remote server returned an error: (400) Bad Request.
when it execute the code of using (var response = (HttpWebResponse)request.GetResponse())
.
My code only worked fine if there is no data-raw required.
Sample json
{"redirect":"www.google.com","subscriptionTypeId":76670001,"marketingCampaign":{"conversionId":12345,"agency":"","medium":"","name":"","source":""}}
My Code
public static bool HTTPRequest(string URL, RequestMethod CallMethod, Hashtable RequestHeaders, string json, ref StringBuilder httpRequestHeader, ref string jsonResponse, bool preparepurchaseFlag)
{
try
{
var request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = CallMethod.ToString();
request.ContentLength = 0;
//Header
if (RequestHeaders != null)
{
foreach (string ParamKey in RequestHeaders.Keys)
{
if (ParamKey == "Content-Type")
{
request.ContentType = RequestHeaders[ParamKey].ToString();
}
else if (ParamKey.ToLower() == "accept")
{
request.Accept = RequestHeaders[ParamKey].ToString();
}
else
{
request.Headers[ParamKey] = RequestHeaders[ParamKey].ToString();
}
}
}
//Body
if (!string.IsNullOrEmpty(json))
{
var encoding = new UTF8Encoding();
var bytes = Encoding.GetEncoding("UTF-8").GetBytes(json);
request.ContentLength = bytes.Length;
using (var writeStream = request.GetRequestStream())
{
writeStream.Write(bytes, 0, bytes.Length);
}
}
//Log Request Header
httpRequestHeader.AppendLine("curl --location --request " request.Method.ToString());
httpRequestHeader.AppendLine(request.RequestUri.ToString());
foreach (string ParamKey in RequestHeaders.Keys)
{
httpRequestHeader.Append("--header ");
httpRequestHeader.AppendLine(ParamKey ": " RequestHeaders[ParamKey].ToString());
}
if (preparepurchaseFlag)
{
httpRequestHeader.Append("--data-raw ");
httpRequestHeader.AppendLine(json);
}
using (var response = (HttpWebResponse)request.GetResponse())
{
var responseValue = string.Empty;
if (response.StatusCode != HttpStatusCode.OK)
{
var message = String.Format("Request failed. Received HTTP {0}", response.StatusCode);
throw new Exception(message);
}
var httpWebResponse = (HttpWebResponse)request.GetResponse();
using (var responseStream = httpWebResponse.GetResponseStream())
{
if (responseStream != null)
using (var reader = new StreamReader(responseStream))
{
responseValue = reader.ReadToEnd();
}
}
jsonResponse = responseValue;
return true;
}
}
catch (WebException e)
{
//catch
return false;
}
catch (Exception ex)
{
//catch
}
}
CodePudding user response:
The usage of HttpWebRequest for new development is not recommended by Microsoft. Instead, you should use the HttpClient class, which simplifies a lot of the process.
Here is a quick wrapper I created to help you get started.
public class HttpClientWrapper
{
private readonly HttpClient _httpClient;
public HttpClientWrapper(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<T> SendRequestAsync<T>(
string relativeUrl,
HttpMethod method,
object? payload,
IDictionary<string, string>? queryParameters,
IDictionary<string, string>? headers)
{
if (string.IsNullOrEmpty(relativeUrl))
{
throw new ArgumentNullException(nameof(relativeUrl));
}
string relativeUrlWithArguments = AddArgumentsToUrl(relativeUrl, queryParameters);
HttpRequestMessage requestMessage = CreateHttpRequestMessage(relativeUrlWithArguments, method, payload, headers);
var response = await _httpClient.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
var textResponse = await response.Content.ReadAsStringAsync();
if (string.IsNullOrEmpty(textResponse))
{
throw new InvalidOperationException("Empty response returned by server.");
}
T? data = JsonSerializer.Deserialize<T>(textResponse);
if (data == null)
{
throw new InvalidOperationException("Failed to deserialize the response to your model.");
}
return data;
}
private static string AddArgumentsToUrl(string url, IDictionary<string, string>? queryParameters)
{
queryParameters ??= new Dictionary<string, string>();
string argumentsString = string.Join("&", queryParameters.Select(arg => $"{arg.Key}={arg.Value}"));
return !string.IsNullOrEmpty(argumentsString) ? $"{url}?{argumentsString}" : url;
}
private static HttpRequestMessage CreateHttpRequestMessage(string relativeUrl,
HttpMethod method,
object? payload,
IDictionary<string, string>? headers)
{
var httpRequestMessage = new HttpRequestMessage(method, relativeUrl)
{
Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
};
if (headers != null)
{
foreach (var header in headers)
{
httpRequestMessage.Headers.Add(header.Key, header.Value);
}
}
return httpRequestMessage;
}
}
Simple usage of the class above can be:
private static HttpClient httpClient = new HttpClient();
var httpClientWrapper = new HttpClientWrapper(httpClient);
var response = httpClientWrapper.SendRequestAsync<ResponseClass>(
relativeUrl: "http://myurl.com",
method: HttpMethod.Post,
payload: YourObjectThatWillBeSerializedToJson,
queryParameters: new Dictionary<string, string>
{
{ "query1", "value1" }
},
headers: new Dictionary<string, string>
{
{ "Accept", "AcceptValue" },
{ "Header2", "Header2Value" }
});
Where ResponseClass
is the type of which the response JSON will be deserialized to. For payload
, you have to send your object that will be serialized to JSON. Query parameters and headers are optional and can be passed as dictionary. You could edit the functionality of this wrapper to your liking. For example, if you want to pass an already serialized JSON as payload, you will have to slightly modify the CreateHttpRequestMessage
method.
Important note: it is recommended to use IHttpClientFactory to manage your HttpClient instances. I have created a static HttpClient
in my example, which could also be fine. But the important part here is to NOT create a new instance of HttpClient
for each request you make.