[C#] Try sending data by HttpClient
Tags: csharp, aspnetcore
Published: 2023-04-05 17:23:15

## Intro
This time, I will try sending data from a console application.

## An ASP.NET Core applicaion for receiving the data
To receive the sent data, I created an ASP.NET Core.

#### [ASP.NET Core] HomeController.cs
```cs
using Microsoft.AspNetCore.Mvc;

namespace SseSample.Controllers;

public record SampleValue(int Id, string Name);
public class WebApiController: Controller
{
    private readonly ILogger<WebApiController> logger;
    public WebApiController(ILogger<WebApiController> logger)
    {
        this.logger = logger;
    }
    [HttpGet]
    [Route("/api/get")]
    public string GetWithQuery([FromQuery] int id, string name)
    {
        return $"Get Query ID: {id} Name: {name}";
    }
    [HttpPost]
    [Route("/api/post/query/url-encoded-content")]
    public string PostWithUrlEncodedContent([FromQuery]int id, string name)
    {
        return $"PostWithUrlEncodedContent ID: {id} Name: {name}";
    }

    [HttpPost]
    [Route("/api/post/body")]
    public string PostWithBody([FromBody] SampleValue value)
    {
        return $"PostWithBody ID: {value.Id} Name: {value.Name}";
    }
    [HttpPost]
    [Route("/api/post/form")]
    public string PostWithForm([FromForm] SampleValue value)
    {
        return $"PostWithForm ID: {value.Id} Name: {value.Name}";
    }
    [HttpPost]
    [Route("/api/post/form/file")]
    public string PostWithFileInForm([FromForm] IFormFile? file)
    {
        return $"PostWithFileInForm Name: {file?.Name} FileName: {file?.FileName} Len: {file?.Length}";
    }
}
```

## Sending data
### GET and POST with URL paramters

I can use "FormUrlEncodedContent" to add URL paramters.

#### [Console] Program.cs
```cs
using System.Text;
using System.Text.Json;
using WebAccessSample;

const string BaseUrl = "http://localhost:5056";
using var httpClient = new HttpClient();
var urlParams = new Dictionary<string, string>{
    { "id", "3" },
    { "name", "hello" }
};
var encodedParams = new FormUrlEncodedContent(urlParams);
var paramText = await encodedParams.ReadAsStringAsync();

Console.WriteLine(paramText);
var getResponse = await httpClient.GetAsync($"{BaseUrl}/api/get?{paramText}");
Console.WriteLine($"/api/get {await getResponse.Content.ReadAsStringAsync()}");

var postWithUrlEncodedContentResponse = await httpClient.PostAsync($"{BaseUrl}/api/post/query/url-encoded-content?{paramText}", 
    new StringContent("", Encoding.UTF8, @"text/plain"));
Console.WriteLine($"/api/post/query/url-encoded-content {await postWithUrlEncodedContentResponse.Content.ReadAsStringAsync()}");
```

#### Results
```
id=3&name=hello
/api/get Get Query ID: 3 Name: hello
/api/post/query/url-encoded-content PostWithUrlEncodedContent ID: 3 Name: hello
```

I also can post like below.
But I have to remove "[FromQuery]" from "PostWithUrlEncodedContent" of the ASP.NET Core application to receive "id" value.

#### [Console] Program.cs
```cs
...
var postWithUrlEncodedContentResponse = await httpClient.PostAsync($"{BaseUrl}/api/post/query/url-encoded-content?{paramText}", encodedParams);
Console.WriteLine($"/api/post/query/url-encoded-content {await postWithUrlEncodedContentResponse.Content.ReadAsStringAsync()}");
```

### POST with HTTP request body
#### SampleValue.cs
```cs
namespace WebAccessSample;
public record SampleValue(int Id, string Name);
```

#### [Console] Program.cs
```cs
...
var sampleValue = new SampleValue(Id: 4, Name: "world");
var sampleValueJson = JsonSerializer.Serialize(sampleValue);
var stringData = new StringContent(sampleValueJson, Encoding.UTF8, @"application/json");

var postWithBodyResponse = await httpClient.PostAsync($"{BaseUrl}/api/post/body", stringData);
Console.WriteLine($"/api/post/body {await postWithBodyResponse.Content.ReadAsStringAsync()}");
```

#### Results
```
...
/api/post/body PostWithBody ID: 4 Name: world
```

### POST with Form data
I can send JSON values as HTTP request body and receive deserialized value in the ASP.NET Core application, but I can't do the same with "MultipartFormDataContent".

#### [Console] Program.cs
```cs
...
var multiFormData = new MultipartFormDataContent();
// I couldn't do this
multiFormData.Add(stringData, "value");

var postWithFormResponse = await httpClient.PostAsync($"{BaseUrl}/api/post/form", multiFormData);
Console.WriteLine($"/api/post/form {await postWithFormResponse.Content.ReadAsStringAsync()}");
```

#### Results
```
...
/api/post/form PostWithForm ID: 0 Name: 
```

I should add values like below.

```cs

var multiFormData = new MultipartFormDataContent();
// OK
multiFormData.Add(new StringContent(sampleValue.Id.ToString()), "value.Id");
multiFormData.Add(new StringContent(sampleValue.Name), "value.Name");

var postWithFormResponse = await httpClient.PostAsync($"{BaseUrl}/api/post/form", multiFormData);
Console.WriteLine($"/api/post/form {await postWithFormResponse.Content.ReadAsStringAsync()}");
```

#### Results
```
...
/api/post/form PostWithForm ID: 4 Name: world
```

I also receive the JSON value as string.
After receiving it, I can manually deserialize it.


#### [Console] Program.cs
```cs
...
var multiFormData = new MultipartFormDataContent();
// OK
multiFormData.Add(stringData, "value");

var postWithFormResponse = await httpClient.PostAsync($"{BaseUrl}/api/post/form", multiFormData);
Console.WriteLine($"/api/post/form {await postWithFormResponse.Content.ReadAsStringAsync()}");
```

#### [ASP.NET Core] HomeController.cs
```cs
...
    [HttpPost]
    [Route("/api/post/form")]
    public string PostWithForm([FromForm] string value)
    {
        var v = JsonSerializer.Deserialize<SampleValue>(value);
        return $"PostWithForm ID: {v?.Id} Name: {v?.Name}";
    }
...
```

#### Results
```
...
/api/post/form PostWithForm ID: 4 Name: world
```

### POST with file data

#### [Console] Program.cs
```cs
...
var multiFormFileData = new MultipartFormDataContent();
byte[] fileData;
using(var memoryStream = new MemoryStream())
{
    using(var fileStream = new FileStream(@"./pics.png", FileMode.Open))
    {
        await fileStream.CopyToAsync(memoryStream);
    }
    fileData = memoryStream.ToArray();
}
// To send file data, I have to add "fileName"
multiFormFileData.Add(new ByteArrayContent(fileData), "file", "pics.png");

var postWithFormFileResponse = await httpClient.PostAsync($"{BaseUrl}/api/post/form/file", multiFormFileData);
Console.WriteLine($"/api/post/form/file {await postWithFormFileResponse.Content.ReadAsStringAsync()}");
```

#### Results
```
...
/api/post/form/file PostWithFileInForm Name: file FileName: pics.png Len: 3872061
```

## Resources
* [FormUrlEncodedContent Class - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.formurlencodedcontent?view=net-8.0)
* [MultipartFormDataContent Class - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.multipartformdatacontent?view=net-8.0)
* [ASP.NET Core and formdata binding with file and json property - StackOverflow](https://stackoverflow.com/questions/45017720/asp-net-core-and-formdata-binding-with-file-and-json-property)
* [C# 今更ですが、HttpClientを使う - Qiita](https://qiita.com/rawr/items/f78a3830d894042f891b)