How to make GET with request body - c#

How do I make a GET query with request body using RestSharp (I'm using RestSharp v. 106.6.10 with .NET Core 2.2). I can do it using WebClient/Postman etc. no problem.
Here's the code failing with {["A non-empty request body is required."]}.
var client = new RestClient("BaseUri");
var request = new RestRequest("URL", Method.GET);
request.AddJsonBody(Body);
client.Execute(request); // {["A non-empty request body is required."]}
This represents a valid use case, pity if it's not supported.
Update: The motivation for having GET requests with body is to avail of get requests having complex parameters, which can't be nicely encoded into a query string. I know people serialize their jsons an put them into a querystrings but I'd rather put it into a request body, considering it's a permissible usage after all.

From my experience AddJsonBody is completely broken (had a multiple times when it wasn't serialize my model just pasting in Body something like MyProject.Models.MyModel or even left body empty). So I use following:
var client = new RestClient("BaseUri");
var request = new RestRequest("URL", Method.GET);
request.AddHeader("Content-Type", "application/json");
string serializedBody = Newtonsoft.Json.JsonConvert.SerializeObject(Body);
request.AddParameter("application/json; charset=utf-8", serializedBody, ParameterType.RequestBody);
client.Execute(request);
UPDATE sorry i wasn't patient when reading you question. Answer is provided for RestSharp not PostSharp

I solved it by using reflection, to trick WebRequest that it is legal to send a body with a GET request.
1.Create Model for body parameters,
public class SampleBodyModel
{
public String name{ get; set; }
public String password{ get; set; }
}
Initialize the request.
SampleBodyModel sampleRequest = new SampleBodyModel
{
name= "name",
password= "password"
};
//initialize the API call
var request = WebRequest.Create("API_END_POINT");
request.ContentType = "application/json";
request.Method = "GET";
var myHttpWebRequest = (HttpWebRequest)request;
// ------- Add these two lines if your using JWT token -------
myHttpWebRequest.PreAuthenticate = true;
myHttpWebRequest.Headers.Add("Authorization", "Bearer " + "TOKEN");
// -----------------------------------------------------------
var type = myHttpWebRequest.GetType();
var currentMethod = type.GetProperty("CurrentMethod",
BindingFlags.NonPublic | BindingFlags.Instance).GetValue(myHttpWebRequest);
var methodType = currentMethod.GetType();
methodType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic |
BindingFlags.Instance).SetValue(currentMethod, false);
//Add the Request body
using (var streamWriter = new
StreamWriter(myHttpWebRequest.GetRequestStream()))
{
streamWriter.Write(Newtonsoft.Json.JsonConvert.SerializeObject(sampleRequest));
}
var response = (HttpWebResponse)myHttpWebRequest.GetResponse();
var responseStream = response.GetResponseStream();
var myStreamReader = new StreamReader(responseStream, Encoding.Default);
var json = myStreamReader.ReadToEnd();
responseStream.Close();
response.Close();

Related

Unable to receive body of a post request in RestSharp 107.1.2

I've just updated my RestSharp application to 107.1.2 and are now unable to send post request.
To test the problem I've set up a small express.js webserver to receive the requests from my C# application. On every post request I made, there is always no body available.
RestClientOptions options = new RestClientOptions("http://127.0.0.1:3000");
RestClient client = new RestClient(options);
RestRequest request = new RestRequest("api/test");
request.AddParameter("test1", "test1");
request.AddParameter("test2", "test2");
request.AddParameter("test3", "test3");
TestResponse response = await client.PostAsync<TestResponse>(request);
The code is based on the QueryString documentation https://restsharp.dev/usage.html#query-string. And yes, the params from the request I receive on the webserver are also empty. To test plain post request, I even tried the following:
RestClientOptions options = new RestClientOptions("http://127.0.0.1:3000");
RestClient client = new RestClient(options);
RestRequest request = new RestRequest("api/test");
const string json = "{ data: { foo: \"bar\" } }";
request.AddStringBody(json, ContentType.Json);
TestResponse response = await client.PostAsync<TestResponse>(request);
I can't find the problem why the requests are sent without an actual post body. In the previous version I've updated from, everything works fine.
Hope you can help me.
Can you please Try this :
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(new { A = "foo", B = "bar" });
or
const string json = "{ data: { foo: \"bar\" } }";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
RestClientOptions options = new RestClientOptions("http://127.0.0.1:3000");
var client = new RestSharp.RestClient(options);
var request = new RestSharp.RestRequest(RestSharp.Method.POST);
request.RequestFormat = RestSharp.DataFormat.Json;
request.AddHeader("Content-Type", "application/json");
const string json = "{ data: { foo: \"bar\" } }";
request.AddBody(JsonConvert.SerializeObject(json));
var response = client.Execute(request);
Try this please
RestClientOptions options = new RestClientOptions("http://127.0.0.1:3000");
var restClient = new RestClient(options);
var restRequest = new RestRequest(Method.POST)
{
RequestFormat = DataFormat.Json
};
const string json = "{ data: { foo: \"bar\" } }";
request.AddBody(JsonConvert.SerializeObject(json));
var result = restClient.Post<TResponse>(restRequest);
if (!result.IsSuccessful)
{
throw new HttpException($"Item not found: {result.ErrorMessage}");
}
return result.Data;
I am having trouble understanding why do you think the request is made without the body. I would expect that the express API gives you no body, but it doesn't mean the request has no body.
I traced your requests, and you can see them here.
The first snippet produces the following request:
As you can see it has the correct content type and the body is correctly formed as form URL encoded body content.
Your second snippet also produces the correct request with JSON body:
It is impossible to say why your server doesn't understand these requests, as you haven't specified what the server needs, but I don't see any issues with those requests per se.
If you you want to add parameters in the request use below way:
RestClient client = new("http://127.0.0.1:3000");
RestRequest restRequest = new("api/test");
restRequest.AddParameter("test1", "test1");
request.AddParameter("test2", "test2");
request.AddParameter("test3", "test3");
RestResponse response = client.ExecuteAsync(restRequest,Method.Post).Result;
Assert.AreEqual(HttpStatusCode.OK, response);
If you you want to add json body in the request use the below way:
RestClient client = new("http://127.0.0.1:3000");
RestRequest restRequest = new("api/test");
string requestBody= "{\"data\":{\"foo\":\"bar\"}}"
restRequest.AddStringBody(requestBody, DataFormat.Json);
RestResponse response = client.ExecuteAsync(restRequest, Method.Post).Result;
Assert.AreEqual(HttpStatusCode.OK, response);

Passing a custom object to a REST endpoint with C#

I have a rest endpoint that accepts a single custom object parameter containing two properties.
Let's call the param InfoParam
public class InfoParam
{
public long LongVar { get; set; }
public string StringVar { get; set; }
}
My code I have is as follows:
infoParam.LongVar = 12345678;
infoParam.StringVar = "abc"
var myRequest = (HttpWebRequest)WebRequest.Create(url);
myRequest.Method = "POST";
var content = string.Empty;
using (var theResponse = (HttpWebResponse)MyRequest.GetResponse())
{
using (var stream = theResponse.GetResponseStream())
{
using (var sr = new StreamReader(stream))
{
content = sr.ReadToEnd();
}
}
}
So I have the InfoParam variable, with the two values, but I can't figure out where to pass it in to the REST endpoint.
You need to turn the object into a stream of bytes that can be added to the Request stream - which will in turn be sent as the HTTP POST body. The format of these bytes needs to match what the server expects. REST endpoints usually expect these bytes to resemble JSON.
// assuming you have added Newtonsoft.JSON package and added the correct using statements
using (StreamWriter writer = new StreamWriter(myRequest.GetRequestStream()) {
string json = JsonConvert.SerializeObject(infoParam);
writer.WriteLine(json);
writer.Flush();
}
You'll probably want to set various other request parameters, like the Content-Type header.
You have to write it int the `Content (and set content-type). Check out How to: Send data by using the WebRequest class
The recommendation is to use System.Net.Http.HttpClient instead.
Please note that you should know what content the server expects ('application/x-www-form-urlencoded`, json, etc.)
The following snippet is from POST JSON data over HTTP
// Construct the HttpClient and Uri. This endpoint is for test purposes only.
HttpClient httpClient = new HttpClient();
Uri uri = new Uri("https://www.contoso.com/post");
// Construct the JSON to post.
HttpStringContent content = new HttpStringContent(
"{ \"firstName\": \"Eliot\" }",
UnicodeEncoding.Utf8,
"application/json");
// Post the JSON and wait for a response.
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(
uri,
content);
// Make sure the post succeeded, and write out the response.
httpResponseMessage.EnsureSuccessStatusCode();
var httpResponseBody = await httpResponseMessage.Content.ReadAsStringAsync();
Debug.WriteLine(httpResponseBody);
In your case the content would be something like this
HttpStringContent content = new HttpStringContent(
JsonConvert.SerializeObject(infoParam), // using Json.Net;
UnicodeEncoding.Utf8,
"application/json");

How to fix null value build.Definition in TFS Queue Build REST API call

I am trying to queue a build from my TFS servers using the TFS Rest API nuget package for c#. However, upon sending the request to the server with the JSON:
"definition":{ "id":63 }
I get a 400 response back with the error:
message=Value cannot be null. Parameter name: build.Definition
I think I am sending the JSON correctly, considering before I was getting errors saying it couldnt be deserialized, or that there wasnt a JSON in the first place.
Can someone help me figure out what is causing this error and how to fix it?
For reference and showing what I have already used as help:
Microsoft Documentation
Queue Build using Powershell
Again Queueing a build in powershell
And several other articles (google "queue build tfs rest api c#")
//post request for queuing the build
var client = new RestClient(websiteName);
var request = new RestRequest("_apis/build/builds?ignoreWarnings=true&sourceBuildId=63&api-version=4.0", Method.POST, DataFormat.Json);
Dictionary<string, string> values = new Dictionary<string, string>();
values.Add("{\"definition\"", "{\"id\":63}}");
request.AddHeader("Authorization", "Basic " + Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "", personalAccessToken))));
request.AddJsonBody(values);
IRestResponse response = client.Execute(request);
"definition":{ "id":63 } isn't valid JSON.
{ "definition":{ "id":63 } } is.
Don't construct JSON as a string, use ConvertTo-Json on an associative array to turn an appropriately-shaped object into JSON, such as
$body = #{
definition = #{
id = $definitionId
}
} | ConvertTo-Json -Depth 100
If you use the Rest API nuget packages, you can use build-in methods to queue a new build, this will much convenient than using HttpClient or other class:
var url = new Uri("http://tfsServer:8080/tfs/MyCollection/");
var connection = new VssConnection(url, new VssCredentials());
var buildServer = connection.GetClient<BuildHttpClient>();
// Get the list of build definitions.
var definition = buildServer.GetDefinitionAsync("teamprojectname", 33).Result;
//It requires project's GUID, so we're compelled to get GUID by API.
var res = buildServer.QueueBuildAsync(new Build
{
Definition = new DefinitionReference
{
Id = definition.Id
},
Project = definition.Project
});
Console.WriteLine($"Queued build with id: {res.Id}");
If you still want to use the URL to trigger, here is also an example:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://tfsServer:8080/tfs/DefaultCollection/TeamProject/_apis/build/builds?api-version=4.0");
request.Credentials = CredentialCache.DefaultNetworkCredentials;
request.Method = "Post";
request.ContentType = "application/json";
Stream stream = request.GetRequestStream();
string json = "{\"definition\":{\"id\":63}}";
byte[] buffer = Encoding.UTF8.GetBytes(json);
stream.Write(buffer, 0, buffer.Length);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Console.Write(response.StatusCode);
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}
Console.Read();

POST json in C# return number

I have a problem with the POST method. I have a REST server programmed in C#, and I want to consume this REST service in C#, but I don't know how. The problem is that my method accepts a POST, receives a JSON payload and returns an HTTPStatusCode and a number:
id_task= planificadorService.CreaTarea(tareaDTO);//tareaDTO is a JSON
if (id_tarea == 0)
{
response = Request.CreateResponse(HttpStatusCode.NotFound, "Cannot create task ");
return response;
}
response = Request.CreateResponse(HttpStatusCode.Created);
response.Content = new StringContent(JsonConvert.SerializeObject(id_task), Encoding.UTF8, "application/json");
return response;
It was easy to do it using the GET method with the WebRequest and HttpWebResponse classes, but I don't know how to do it with the POST method. After many attempts, I ended up with something like this:
public void PostTareas(Tarea tarea)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url_base + "/v1/tareas");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
string json = JsonConvert.SerializeObject(tarea);
var client = new HttpClient()
{
BaseAddress = new Uri(url_base + "/v1/tareas")
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response;
response.Content= new StringContent(JsonConvert.SerializeObject(tarea).ToString(), Encoding.UTF8, "application/json");
response = client.PostAsync(url_base + "/v1/tareas", json)).Result;
}
I'm on the right track? How can I do this so that I am able to access the Json content? Thanks
P.D- Excuse my english, it is not my native language and I know there may be faults in expressing myself
With WebRequest you need to write the JSON in the POST request payload, use WebRequest.GetRequestStream:
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url_base + "/v1/tareas");
...
using(var requestStream = request.GetRequestStream()) {
// Write the serialized json into the stream, it will be send as payload
using(TextWriter writer = new StreamWriter(requestStream)) {
writer.WriteLine(JsonConvert.Serialize(tarea));
}
}
var response = request.GetResponse();
or you can use HttpClient and call PostAsync, as you're doing in your second part of your code. Either way is fine, but stick to one :)
You should also consider using a high(er) level library, like RestSharp. Ultimately consider exposing your server API with Swagger via Swashbuckle, generate a client with swagger-codegen and spend your time at the higher level abstraction of the API, not the HTTP/Json layer.

Can't access Web of Trust (WoT) API w/ JSON.Net

I'm new to JSON & am using VS 2013/C#. Here's the code for the request & response. Pretty straightforward, no?
Request request = new Request();
//request.hosts = ListOfURLs();
request.hosts = "www.cnn.com/www.cisco.com/www.microsoft.com/";
request.callback = "process";
request.key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string output = JsonConvert.SerializeObject(request);
//string test = "hosts=www.cnn.com/www.cisco.com/www.microsoft.com/&callback=process&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
try
{
var httpWebRequest = (HttpWebRequest) WebRequest.Create("http://api.mywot.com/0.4/public_link_json2?);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = output;
streamWriter.Write(json);
}
var httpResponse = (HttpWebResponse) httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var responseText = streamReader.ReadToEnd();
}
}
catch (WebException e)
{
MessageBox.Show(e.ToString());
}
//response = true.
//no response = false
return true;
}
When I run this, I get a 405 error indicating method not allowed.
It seems to me that there are at least two possible problems here: (1) The WoT API (www.mywot.com/wiki/API) requires a GET request w/ a body, & httpWebRequest doesn't allow a GET in the httpWebRequest.Method; or (2) the serialized string isn't serialized properly.
NOTE: In the following I've had to remove the leading "http://" since I don't have enough rep to post more than 2 links.
It should look like:
api.mywot.com/0.4/public_link_json2?hosts=www.cnn.com/www.cisco.com/www.microsoft.com/&callback=process&key=xxxxxxxxxxxxxx
but instead looks like:
api.mywot.com/0.4/public_link_json2?{"hosts":"www.cnn.com/www.cisco.com/www.microsoft.com/","callback":"process","key":"xxxxxxxxxxxxxxxxxxx"}.
If I browse to:api.mywot.com/0.4/public_link_json2?hosts=www.cnn.com/www.cisco.com/www.microsoft.com/&callback=process&key=xxxxxxxxxxxxxx; I get the expected response.
If I browse to: api.mywot.com/0.4/public_link_json2?{"hosts":"www.cnn.com/www.cisco.com/www.microsoft.com/","callback":"process","key":"xxxxxxxxxxxxxxxxxxx"}; I get a 403 denied error.
If I hardcode the request & send as a GET like below:
var httpWebRequest = (HttpWebRequest) WebRequest.Create("api.mywot.com/0.4/public_link_json2? + "test"); it also works as expected.
I'd appreciate any help w/ this & hope I've made the problem clear. Thx.
Looks to me like the problem is that you are sending JSON in the URL. According to the API doc that you referenced, the API is expecting regular URL encoded parameters (not JSON), and it will return JSON to you in the body of the response:
Requests
The API consists of a number of interfaces, all of which are called using normal HTTP GET requests to api.mywot.com and return a response in XML or JSON format if successful. HTTP status codes are used for returning error information and parameters are passed using standard URL conventions. The request format is as follows:
http://api.mywot.com/version/interface?param1=value1&param2=value2
You should not be serializing your request; you should be deserializing the response. All of your tests above bear this out.

Categories

Resources