Sending file in API request .NET - c#

I have an enpoint that I send request via Postman form-data. Request has 2 keys.
message : string
file: File
this works ok. So, I want to send the same request in C# code. In c# I just need to choose file from local, somehow convert it to proper format, assign it to file property in request Model, I mean I can not change the logic how request is sent. I must not write additional logic to HttpRequest or MultipartFormDataContent etc (I can not even see this part how it is sent). Just need to add Model to this request body.
I have model RequestModel
public class RequestModel
{
[JsonProperty("message")]
public string Message {get; set; }
[JsonProperty("file")]
public whateverType File {get; set; }
}
message part works, so I need to assign local file to File property. how can I do that. I tried almost everything but it does not work. Thanks.

You don't need to keep the file in your model.
this is an example to post a model just by knowing the file name.
var model = new RequestModel();
// ... fill the model with some data
model.Message = "test";
model.File = #"c:\test.jpg";
using var httpClient = new HttpClient();
{
var form = new MultipartFormDataContent();
form.Add(new StringContent(model.Message), nameof(RequestModel.Message));
var bytes = File.ReadAllBytes(model.File);
form.Add(new ByteArrayContent(bytes, 0, bytes.Length), "profile_pic", "hello1.jpg");
var response = await httpClient.PostAsync("PostUrl", form);
response.EnsureSuccessStatusCode();
var sd = response.Content.ReadAsStringAsync().Result; // you should use await if your method is async ...
}
public class RequestModel
{
public string Message { get; set; }
public string File { get; set; } // simply use file-path
}

Related

JsonConvert.DeserializeObject File into a model class web api .net core

I'm creating an WebAPI with C# .netcore. I have a method which gets file stream (different types) from a URL based on file id
function async Task<T> GetFile(string id) {
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri("serverURL" + id);
request.Method = HttpMethod.Get;
HttpResponseMessage response = await httpClient.SendAsync(request,
HttpCompletionOption.ResponseContentRead, cancelToken);
var result = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(result);
}
I want to return the result into a model like this
public class FileModel
{
public HttpResponseMessage File { get; set; }
}
I convert the result after that to base64 to get the file.
I get this error
Unexpected character encountered while parsing value: B. Path '', line 0, position 0
because JsonConvert cannot convert the result into my FileModel class. I cannot use HttpResponseMessage instead of in the method, the method should be generic.
Thanks
Have you check, if you Json is in correct format.
It seems like, you "result" is not in the correct form.
JsonConvert.DeserializeObject(result) is expecting Proper Json, otherwise it will return error.
To check if the Json structure is proper: http://jsonlint.com/
To generate class from Json structure: https://app.quicktype.io/?l=csharp
Your FileModel should not have a property of type HttpResponseMessage because that is not what the Json deserializer will return. It will try to convert that JSON into a class based on the generic model (T) you are using. The structure of your generic model (T) must match the JSON that is being returned.
For example, if your json looks like this:
{
field1: "value1",
field2: 123
}
Your model should look like this
public class MyModel {
string field1 { get; set;}
int field2 { get; set;}
}
If the results of your api calls are different models, then you need to create a new model for each call. You shouldn't try and use the same model for all of your calls.

No result returned when calling async local request in a Unit Test

Can you see anything obvious I'm missing? I'm trying to create a unit test that calls a local web api but I'm not getting what is expected.
Article resource I'm using is here.
[TestMethod]
public void TestAutocomplete()
{
string resource = "api/products?parm=test";
Task<Class1> product = GetProductAsync(resource);
}
async Task<Class1> GetProductAsync(string path)
{
HttpClient client = new HttpClient();
// Update port # in the following line.
client.BaseAddress = new Uri("http://localhost:22292/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(path);
Class1 product = null;
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync<Class1>();
}
return product;
}
...
public class Class1
{
public string text { get; set; }
public string street_line { get; set; }
public string city { get; set; }
public string state { get; set; }
}
Update:
After changing:
Task<Class1> product = GetProductAsync(resource);
to
Class1 product = GetProductAsync(resource).Result;
{"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'AddressValidationAPI.Tests.Class1' because the type requires a JSON
object (e.g. {\"name\":\"value\"}) to deserialize correctly.\r\nTo fix
this error either change the JSON to a JSON object (e.g.
{\"name\":\"value\"}) or change the deserialized type to an array or a
type that implements a collection interface (e.g. ICollection, IList)
like List that can be deserialized from a JSON array.
JsonArrayAttribute can also be added to the type to force it to
deserialize from a JSON array.\r\nPath '', line 1, position 1."}
Thanks for all the comments under the OP. Here's the final unit test. Thanks everyone.
[TestMethod]
public void TestAutocomplete()
{
string resource = "api/addresses";
List<AutocompleteSuggestions> suggests = GetAutocompleteSuggestions(resource).Result;
Assert.AreEqual(1, suggests.Count);
}
async Task<List<AutocompleteSuggestions>> GetAutocompleteSuggestions(string path)
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("http://localhost:22292/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.GetAsync(path);
List<AutocompleteSuggestions> suggests = null;
if (response.IsSuccessStatusCode)
{
suggests = await response.Content.ReadAsAsync<List<AutocompleteSuggestions>>();
}
return suggests;
}
}
Notice that Task<Class1> GetProductAsync does not return Class1 (note that you should name it properly), but a Task<Class1>, and you do not have any code to execute it to get the result.
You have to execute it, either by the recommended proper way:
[TestMethod]
public async Task TestAutocomplete()
{
string resource = "api/addresses";
List<AutocompleteSuggestions> suggests = await GetAutocompleteSuggestions(resource);
Assert.AreEqual(1, suggests.Count);
}
Another small note, it's also recommended to rename GetAutocompleteSuggestions into GetAutocompleteSuggestionsAsync so you know you should await it.
If you absolutely cannot switch it to async method (for example, in Main method - which recently supported too, or when your Test framework does not support it), use:
List<AutocompleteSuggestions> suggests = GetAutocompleteSuggestions(resource).Result;
Further reference: Asynchronous programming with async and await (C#)
The latter error is simply a mis-structure in your JSON/model class. You just have to check and reconcile between the two.

c# POST api fuss

I'm building a web Api to catalog my firms bug report onto a server, and I'm having trouble with the post request that I'm doing.
As I've coded the clients they should be either sending bug reports that are formatted like this
public partial class JitCollect {
public DateTime? Timestamp { get; set; }
public string Jit { get; set; }
public int? ProjectId{ get; set; }
}
or they could be sending strings with status reports like "OK" or whatever. My main problem is that if I send the bug reports as "application/x-www-form-urlencoded" and prepending a '=' to my body like I saw online, I lose the DateTime format that NewtonSoft is expecting on the other side:
before "2018-08-14T08:50:17.5608444+02:00"
after "2018-08-14T08:50:17.5608444 02:00"
I could hardcode something to put the '+' back but that's beside the point, I'm interested in how to properly accomplish what I'm trying to do.
if I instead try to send the data as "application/json", I always get empty data on the other side even specifying the from body attribute and the object type (which is not ideal for me because I want to be able to send plain strings as well)
[HttpPost]
public string Post([FromBody] List < JitCollect > jsonPost)
any idea what is the best way to do this? here's one of the many post functions I tried to use
public static void postRequest(string data, string address) {
using (var client = new HttpClient()) {
client.BaseAddress = new Uri(address);
data = $"={data}";
//client.DefaultRequestHeaders.Add("token", token);
var buffer = System.Text.Encoding.UTF8.GetBytes(data);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
//byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var result = client.PostAsync("", byteContent).Result;
string resultContent = result.Content.ReadAsStringAsync().Result;
}
}
I made a test with asp.net core web api, it passes the datetime correctly.
Controller
[HttpPost]
public object Post([FromBody]List<JitCollect> jsonPost)
{
var resul = jsonPost.FirstOrDefault().Timestamp.Value.ToString("yyyy’-‘MM’-‘dd’T’HH’:’mm’:’ss.fffffffK");
return new List<JitCollect>() {
new JitCollect{ ProjectId = 1, Jit = "J1", Timestamp = DateTime.Now }
};
}
Request from postman
Result From Controller

Example of Firebase REST API for C#

I have been trying to find examples of CRUD Firebase database for C#. I have researched some websites and tried doing on my own, but not all are complete nor updated examples.
May I know if there is a complete example of such?
I have tried using HttpWebResponse to query from Firebase.
So this is my sample code
public void getUser()
{
var request = (HttpWebRequest)WebRequest.Create(apiUrl + "user.json?orderBy=\"email\"&equalTo=" + "\"" + userId + "\"");
request.Method = "GET";
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
using (var response = (HttpWebResponse)request.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
var js = new JavaScriptSerializer();
var objText = reader.ReadToEnd();
var result = JsonConvert.DeserializeObject<List<User>>(objText);
}
}
}
My User class
public string email { get; set; }
public string firstName { get; set; }
public string #group { get; set; }
public string lastName { get; set; }
However, when I call getUser, it returns me null. But inside the objText, there is text in it.
E.g.
{"12345678":{"email":"user#hotmail.com","firstName":"Alan","group":"N","lastName":"Paul"}}
You are trying to get the User data from the Firebase database and you are able to get the data successfully too.
The issue is with the JSON deserialization. The JSON you are getting from Firebase can not be deserialized to List<User>. Also it can not be deserialized to a single object of User.
The reason for this is the format of the JSON. As per the JSON the target class should have a property with name 12345678 and the type of the property should be User. And your User class not like that. I assume that the value "12345678" is the userId.
What you can do is to convert the JSON to a dictionary with string as Key and User as value holding type and then get the User object from the dictionary.
Consider following code. (I am writing only necessary code here)
var objText = reader.ReadToEnd();
var usersDictionary = JsonConvert.DeserializeObject<Dictionary<string, User>>(objText);
// I assume that the value `12345678` represents the UserId.
// If my assumption is right, following code gives you the user object from the dictionary.
var result = usersDictionary[userId];
//this line of code may throw an exception coz the dictionary does not have the userId in it
// this could be due to Firebase did not return any data in "objText".
This way you can get the user object out or the response text from the Firebase.

Passing JSON to Web.API works with Fiddler but not in code

I'm trying to pass my JSON to Web.API service. The sending works well with Fiddler when I set to POST and I get value inside [FromBody ] argument:
Http/1.1
User-Agent: Fiddler
content-type: application/json; charset=utf-8
Host: http://localhost:27701/api/myList
Content-Length: 883
But when I use this C# code to post JSON, then [FromBody ] argument is empty:
HttpContent content = new StringContent(data);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:27701/api/");
HttpResponseMessage response = client.PostAsync("myList", content).Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsAsync<string>().Result;
s = result;
}
The data part is the exact JSON in both Fiddler and my code and the controller called in both calls.
This is my JSON:
{
"Id":0,
"Count":0,
"StartDate":"\\/Date(-62135596800000)\\/",
"Address":{
"Id":0,
"State":"test",
"City":"test"
}
}
One thing is if I don't put ' (single quotation) in both side of string inside fiddler the [FromBody] argument is empty, but if i put those on C# sample the respond is 500 server error.
You haven't posted your receiving method code, but based on provided data it should be a method with one argument which is an object that represents your JSON. In this case you don't need to use FromBody attribute at all.
If you check this article you can find there that:
By default, Web API uses the following rules to bind parameters:
If the parameter is a “simple” type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int,
bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal,
and string, plus any type with a type converter that can convert from
a string.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
I created a models based on your JSON:
public class Address
{
public int Id { get; set; }
public string State { get; set; }
public string City { get; set; }
}
public class RootObject
{
public int Id { get; set; }
public int Count { get; set; }
public string StartDate { get; set; } // Keeped as string for simplicity
public Address Address { get; set; }
}
And then really simple method that can receive such JSON:
public RootObject Post(RootObject req)
{
return req;
}
Then I tested it both with Fiddler:
Method:
POST
Headers:
Content-Type: application/json
Request Body:
{"Id":0,"Count":0,"StartDate":"\\/Date(-62135596800000)\\/","Address":{"Id":0,"State":"test","City":"test"}}
And C# code:
var data = "{\"Id\":0,\"Count\":0,\"StartDate\":\"\\/Date(-62135596800000)\\/\",\"Address\":{\"Id\":0,\"State\":\"test\",\"City\":\"test\"}}";
using (var client = new HttpClient())
{
HttpContent content = new StringContent(data);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response =
client.PostAsync("http://my.url", content).Result;
var result = response.Content.ReadAsStringAsync().Result;
}
In both cases I was able to get sent object back.
Some tips:
When you send JSON with Fiddler you should not use any escaping
for request body. Just enter valid JSON and that's it.
In C# code if you need to declare a string variable with JSON you
will need to use escaping. For example var json = "\"a\":\"b\""; or
var json = #"""a"":""b""";. If you received JSON from somewhere
else then you don't need to do nothing.
You should never encase JSON with ' chars.

Categories

Resources