string parameter in controller is null when using webclient - c#

When I use WebClient to call a function in a controller
using (WebClient client = new WebClient())
{
client.BaseAddress = "http://localhost/";
client.Headers.Add("Content-Type", "application/json");
client.UploadString("api/test", HttpMethod.Put.ToString(), stringParameter);
}
I called the function in controller successfully but the "stringParameter" in controller is empty like the following code:
[Route("api/test")]
[HttpPut]
public async Task<IHttpActionResult> Test([FromBody] string stringParameter)
{
if (!ModelState.IsValid)
{
// the flow goes here
return BadRequest(ModelState);
}
// ...
}
I would like to know where is the mistake(s) I made and how to fix the problem(s). Thank you very much.
Remark: "stringParameter" is fine if it is numeric such as "123" but not work if it is "A123" or "B123".

As you're setting content type to "application/json" you should be sending a valid JSON value rather then raw string. A number is JSON and in plain text is the same, so that's why it works.
Try serializing the text to JSON before sending:
JsonConvert.ToString(stringParameter);
(I'm using Newtonsoft.Json nuget package here)
Alternatively you can try to use content type text/plain, but I'm not sure if your web api is configured to handle that by default.

Related

"Pass through" controller action (gets and returns JSON) in .NET Core 3.1

Someone's probably done this before but I can't seem to formulate the question properly to find results. I want to make AJAX calls from a view, but I can't directly call the external API from javascript because there's a key that I can't expose. My idea is to have another controller action that I call from the page that calls the actual external REST API I want to get data from and just passes it on as a JSON. I see lots of examples of getting a JSON through C# and deserializing it but not many where you get a JSON and then return it and consume it from the view. Any help appreciated.
public JsonResult GetStuff()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("Stuff/?Id=" + id).Result;
*code to take response and pass it on as a JSON that I can consume from Javascript
}
Here is what I recommend.
[HttpGet("myapi/{id}");
public async Task MyApi(int id) {
// Replace these lines as needed to make your API call properly.
using HttpClient client = new() {
BaseAddress = REMOTE_SERVER_BASE
}
// Make sure to properly encode url parameters if needed
using HttpResponseMessage response = await client.GetAsync($"myapi/{id}");
this.HttpContext.Response.StatusCode = (int)response.StatusCode;
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers) {
this.HttpContext.Response.Headers[header.Key] = new StringValues(header.Value.ToArray());
}
await response.Content.CopyToAsync(this.HttpContext.Response.Body);
}
This will copy all the common response fields such as status code, headers, and body content, over to your response.
This code isn't tested so you might have to tweak it a bit but it should be enough to get you started.

API Controller unable to read Json from POST body

Background
I'm sending JSON in my body to my API controller but keep getting the following error.
{"":["Unexpected character encountered while parsing value: {. Path
'', line 1, position 1."]}
My HTTP request
HttpClient client = new HttpClient();
HttpRequest httpRequest;
HttpResponseMessage httpResponse = null;
httpRequest = new HttpRequest("", HostnameTb.Text, null);
var values = new Dictionary<string, string>
{
{ "APIKey", APIKeyTb.Text }
};
string json = JsonConvert.SerializeObject(values);
StringContent content = new StringContent(json.ToString(), Encoding.UTF8, "application/json");
httpResponse = client.PostAsync(HostnameTb.Text, content).Result;
var responseString = await httpResponse.Content.ReadAsStringAsync();
My Controller looks like this.
[HttpPost]
public void Post([FromBody] string value)
{
//Never gets here.
}
The Json in the body.
{"APIKey":"1283f0f8..."}
Question
I would prefer to use the .Net Core [From Body] functionality, rather than getting the content manually.
I would expect the JSON string to be available in the string Value parameter.
What am I missing?
ASP.NET Core tries to deserialize {"APIKey":"1283f0f8..."} from JSON into a string value, and fails, because it expects the input to be a valid JSON string.
In other words, if your body was "{\"APIKey\":\"1283f0f8...\"}" you would have the JSON string in the input variable as you expect.
In order to get the APIKey value without changing the HTTP request, create an input type:
public class Input
{
public string ApiKey { get; set; }
}
and use that as the input of your controller action:
[HttpPost]
public void Post([FromBody] Input input)
{
var apiKey = input.ApiKey;
// etc
}
Alternatively, change your HTTP request to send a string:
// ...
var content = new StringContent(JsonConvert.SerializeObject(json), Encoding.UTF8, "application/json");
// ...
Note the use of JsonConvert.SerializeObject() instead of ToString(); "foo".ToString() is still just "foo", while you want "\"foo\"".
That's not how this works. [FromBody] invokes a serializer to deserialize the request body. Then, the modelbinder attempts to bind that to the param. Here, it cannot do that because you're binding to a string, and the request body is a dictionary. Essentially what's happening under the hood (pseudo-code) is:
value = JsonConvert.DeserializeObject<string>(dictionaryAsJson);
You're getting a deserialization error from JSON.NET because it can't parse the JSON into a string.
If you want the value as a string, then you should post is as something like text/plain instead of application/json. Otherwise, you'll need to bind to a type that actually represents the JSON object coming in, which would be a Dictionary<string, string> here.
I had the same issue with ASP.NET Core 3.1. I was POSTing a JSON to my API controller that looked like:
public JsonResult Save([FromBody]MainDetails obj){ ... }
The problem in my case was that a property ChildDetails.StartDate of my MainDetails object was of type DateTime, and I was sending a null value in JSON. This was causing the deserialization to fail at the controller. I changed the property type to DateTime? from DateTime to make it work.
Basically, one needs to check and ensure that the JSON that you POST is valid for the target object to which you are deserializing it. If you have non-nullable properties for which the value sent in JSON is null, then your deserialization will fail (without telling you why).
Hope this helps.

Receiving XML in HttpPost Using C#

I thought this would be pretty easy, but it's just not - at least for me. I am trying to send an XML string to a REST endpoint. At this time, the only thing the endpoint has to do is log the XML to a file or database. The XML itself is unknown, it could be literally any length and have any number of nodes. So it really needs to be treated as a string.
My problem is that I cannot determine how to receive the XML/string in the Post method. I am using the RestSharp library.
Here is the Post method I am using; very simple. I removed logging code and try/catch code to keep it simple.
[HttpPost]
public IHttpActionResult Post([FromBody] string status)
{
// Log the post into the DB
LogPost(status);
}
The code to perform the post:
public void TestPost()
{
IRestResponse response;
try
{
// Get the base url for
var url = #"http://blahblah/status";
// Create the XML content
object xmlContent = "<XML><test><name>Mark</name></test></XML>";
var client = new RestClient(url);
var request = new RestRequest(Method.POST);
// Add required headers
request.AddHeader("Content-Type", "text/plain");
request.AddHeader("cache-control", "no-cache");
request.AddJsonBody(xmlContent);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
response = client.Execute(request);
}
catch (Exception ex)
{
...
}
}
The problem: the status parameter received by the post is, simply, "Mark". The full XML is missing! I need the entire XML string.
I have tried a few different variations of this. Changing the content-type to "application/xml", "application/json", etc. Nothing is working.
I have tried using request.AddXmlBody(statusObject), and request.AddBody(statusObject). Both were unsuccessful.
I have even tried sending the XML using request.AddHeader() with no luck. What am I missing. There must be something obvious that I'm not getting.
a) you must configure Web API to use XmlSerializer in your WebApiConfig.Register. Otherwise Web API uses the DataContractSerializer by default.
File: App_Start\WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Formatters.XmlFormatter.UseXmlSerializer = true; //HERE!
...
}
b) you need to define a class for your xml
public class test { public string name { get; set; } } //BASED ON YOUR XML NODE
[HttpPost]
public IHttpActionResult Post([FromBody] string status)
{
}
c) if you need to work with a simple string, change POST method
public void Post(HttpRequestMessage request)
{
string body = request.Content.ReadAsStringAsync().Result;
}
d) invoke from restsharp client
string xmlContent = "<test><name>Mark</name></test>";
var client = new RestClient(url);
var request = new RestRequest(Method.POST);
request.AddParameter("application/xml", xmlContent, ParameterType.RequestBody);
var response = client.Execute(request);
For "some" reason request.AddParameter takes the first param as ContentType(not the Name)
https://github.com/restsharp/RestSharp/issues/901
Did you tried to send the request with
Content-Type: application/xml; charset="utf-8"
instead of text\plain?

Sending JSON from WebApi to Android

I have problem understanding sending Json from WebApi to Android. I know how to make requests from Json, but don't know how to make Task that waits for request and return Json. I have json made from Mongo DB query with JsonConvert.SerializeObject.
I have been searching and found this that I have for now, but this isn't what I need I think, I don't have url in PostAsync, I just want to wait for request and respond with Json, where I need to look for:
public async Task<HttpStatusCode> SendAsync()
{
var content = new StringContent(
jsonString,
Encoding.UTF8,
"application/x-www-form-urlencoded");
HttpResponseMessage response = await _httpClient.PostAsync(_url, content);
return response.StatusCode;
}
You need to return an IHttpActionResult implementation. For example:
public async Task<IHttpActionResult> SendAsync()
{
// Object passed to Ok will be automatically serialized to JSON
// if response content type is JSON (and, by default, this is the serialization
// format in ASP.NET WebAPI)
return Ok(new { text = "hello world" });
}

How to pass classic asp dictionary to .NET RESTful Web Api

I am having issues passing data parameters from a classic asp application to a .NET Web API. It seems that no matter what I do I cannot access the parameter inside the .NET web API. I have included the code I am using below:
The Classic ASP code:
Public Function GetResponse (HTTPMethod, ReqURL, QueryParamsOrArgs)
Set XMLHTTP = Server.CreateObject("MSXML2.ServerXMLHTTP")
If HTTPMethod = "GET" Then
ReqURL = ReqURL & "?" & QueryParamsOrArgs
QueryParamsOrArgs = ""
End If
XMLHTTP.open HTTPMethod , ReqURL, false
XMLHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
XMLHTTP.setRequestHeader "AuthToken", ServiceUrl_AuthToken
XMLHTTP.send(QueryParamsOrArgs)
If XMLHTTP.Status = 200 Then
GetResponse = XMLHTTP.responseText
Else
GetResponse = ""
End If
End Function
Public Function TestPOST(url)
mydata = "value1=1&value2=2&value3=3"
TestPOST = GetResponse("POST", url, mydata)
End Function
In the calling ASP page:
dim oDataUtils : Set oDataUtils = (New DataUtils)
myResponse = oDataUtils.TestPost(ServiceUrl_Base & "EventData/Post")
The .NET Web API Action Method:
[HttpPost]
public HttpResponseMessage Post([FromBody]string value)
{
StringContent sc = new StringContent(value);
sc.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage resp = new HttpResponseMessage();
resp.Content = sc;
return resp;
}
No matter what I send, the value of the parameter "value" inside the api method is always null. Ultimately I would love to be able to send an entire dictionary or parameters to this method, but cannot even get the most simple component (a string) to pass in. What am I missing here?
My ultimate goal would be for something like this to work:
[HttpPost]
public HttpResponseMessage Post(Dictionary<string, object> data)
{
// Do something with the dictionary and then return a response...
StringContent sc = new StringContent("test");
sc.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage resp = new HttpResponseMessage();
resp.Content = sc;
return resp;
}
but I will take either option at this point...
The "issue" is: [FromBody]
public HttpResponseMessage Post([FromBody]string value)
It is for "simple" type, and translated means, just one value. Additionally, the format the API expects using FromBody is =value (notice the missing "key").
e.g.
foo=bar //this will fail "null"
=bar //this is good (no key)
This will explain it in detail even if the topic is about jquery, it will give you insight to the behavior of FromBody.
Use FormDataCollection...
Hth....
It might work if you pass proper json instead of just string ,
form you json like
mydata="{'value':'value1=1&value2=2&value3=3'}"
You can also pass dictionary of like
var data={};
data.Value1="1";
data.Value2="2";
data.Value3="3";
Remember the name of param in action method and name of json key must be same,here 'values
mydata={values:data};
[HttpPost]
public HttpResponseMessage Post(Dictionary<string, string> values)

Categories