I have a WebAPI post method:
[HttpPost]
public void Submit(ICollection<IFormFile> formFiles)
{
foreach (var formFile in formFiles)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(#"1.txt", FileMode.Create))
{
formFile.CopyTo(stream);
}
}
}
}
From Client I am trying to do :
HttpClient client = new HttpClient();
CustomFormFile fromFile = new CustomFormFile(new FileInfo("ds.txt"));
ICollection<IFormFile> listFormsFiles = new List<IFormFile>();
listFormsFiles.Add(fromFile);
var response = client.PostAsJsonAsync(requestURL, listFormsFiles)
When I am trying to set a breakpoint on my WebApi it is not getting hit, but if I change the PostAsync to following, breakpoint gets hit:
var response = client.PostAsJsonAsync(requestURL, fromFile);
But on service, this becomes null as it was expecting a list but got a single object.
Any pointers what am I missing?
Related
I have code that is working on one of my aspx.cs pages but not this one. The issue is that The JsonConvert.DeserializeObject is returning a list of null values.
This is the code
private async Task getProj(List<ProjectsObj> temp)
{
using (var client1 = new HttpClient())
{
client1.BaseAddress = new Uri("http://localhost:57578/");
client1.DefaultRequestHeaders.Accept.Clear();
client1.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage resp = await client1.GetAsync("api/projects").ConfigureAwait(false);
if (resp.IsSuccessStatusCode)
{
dynamic result = await resp.Content.ReadAsStringAsync();
List<ProjectsObj> root = JsonConvert.DeserializeObject<List<ProjectsObj>>(result);
foreach (ProjectsObj proj in root)
{
temp.Add(proj);
}
}
}
}
I'm trying to build a simple twitter HttpClient and MySQL application using .Net Core 3.1, but I'm seeing an issue where result object becomes null before I'm done handling it. What should be the correct way to handle this?
Sample code:
using (HttpClient httpClient = new HttpClient())
{
httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var requestUri = "https://api.twitter.com/2/tweets/search/stream";
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer <token>");
var stream = httpClient.GetStreamAsync(requestUri).Result;
using (var reader = new StreamReader(stream))
{
//while (!reader.EndOfStream)
while (reader.Peek() >= 0)
{
//We are ready to read the stream
var ResultObject = JsonConvert.DeserializeObject<Tweet>(reader.ReadLine());
Console.WriteLine(ResultObject);
if (ResultObject != null) // <== ResultObject disappears after this :: NullReferenceException
{
Console.WriteLine(ResultObject);
string sQuery = $"INSERT INTO MySQLTable (tweet_id,text) VALUES ({ResultObject.data.id},\"{ResultObject.data.text}\");";
Client.NonQuery(sQuery);
Console.WriteLine(Client.Query("SELECT * FROM MySQLTable;"));
};
}
}
}
public class Tweet
{
public TweetData data;
}
public class TweetData
{
public string id;
public string text;
}
(This issue did not appear in .Net 5.0)
Problem was in string sQuery.
I was trying to .Select() on a null member. Works perfectly fine when there is validation present for null members.
So I've looked around for an answer for this but nothing I've found even comes close to solving it.
I'm trying to set up a Post method on my Web API but no matter what I do it just gives me an internal server error.
I've tried adding [FromBody] (it's a simple type).
HttpClient client {get;set;}
public APICall()
{
client = new HttpClient
{
BaseAddress = new Uri("http://localhost:1472/api/")
};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf"));
}
public void PostTimeTaken(long timeTaken)
{
var response = client.PostAsJsonAsync("Logging", timeTaken).Result;
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.ReasonPhrase);
}
}
and then my controller action looks like this:
public void Post([FromBody] long timeTaken)
{
_api.DataBuilder.NumberOfAPICalls += 1;
_api.DataBuilder.ResponseTimes.Add(timeTaken);
}
I get no error message that could actually explain what's going on, just "Internal server error"
------SOLVED-------
Just in case anyone stumbles across this looking for the same answer, the issue was I was sending the data to the server in an incorrect format, it needed to be ProtoBuf serialised first, code snippet for anyone it might help:
public void PostToAPI(int ThingToSend)
{
using (var stream = new MemoryStream())
{
// serialize to stream
Serializer.Serialize(stream, ThingToSend);
stream.Seek(0, SeekOrigin.Begin);
// send data via HTTP
StreamContent streamContent = new StreamContent(stream);
streamContent.Headers.Add("Content-Type", "application/x-protobuf");
var response = client.PostAsync("Logging", streamContent);
Console.WriteLine(response.Result.IsSuccessStatusCode);
}
}
using (var client = new HttpClient())
{
string url = "http://localhost:7936";
client.BaseAddress = new Uri(url);
var jsonString = JsonConvert.SerializeObject(contentValue);
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var result = await client.PostAsync("/Api/Logger/PostActionLog", content);
string resultContent = await result.Content.ReadAsStringAsync();
}
Have you tried to convert
long timeTaken to A model like;
public class TimeModel {
public long TimeTaken {get;set;}
}
public void Post([FromBody] TimeModel time){
// Do Stuff
}
Here the code of creating a simple server
baseUrl = "http://localhost:1472/"; // change based on your domain setting
using (WebApp.Start<StartUp>(url: baseUrl))
{
HttpClient client = new HttpClient();
var resp = client.GetAsync(baseUrl).Result;
}
Here some changes in your code
var requestData = new List<KeyValuePair<string, string>> // here
{
new KeyValuePair<string, string>( "Logging",timeTaken),
};
Console.WriteLine("request data : " + requestData);
FormUrlEncodedContent requestBody = newFormUrlEncodedContent(requestData);
var request = await client.PostAsync("here pass another server API", requestBody);
var response = await request.Content.ReadAsStringAsync();
Console.WriteLine("link response : " + response);
Pls add your controller
[HttpPost] // OWIN - Open Web Interface for .NET
public HttpResponseMessage Post([FromBody] long timeTaken)
{
_api.DataBuilder.NumberOfAPICalls += 1;
_api.DataBuilder.ResponseTimes.Add(timeTaken);
return Request.CreateResponse(HttpStatusCode.OK); //Using Post Method
}
I am calling an API Post method, however, I am not sure what I am doing wrong but the value in the API is always null. The method I am calling the API from is below. When I hit this I can see Ids is list of ints with 5 values for example.
private void Save(List<int> Ids)
{
var myAPI = ConfigurationManager.AppSettings["MyAPI"];
string myIds = string.Join(",", Ids);
using (var client = new HttpClient())
{
int result = client.PostAsync(myAPI, new { test = myIds }, new JsonMediaTypeFormatter())
.Result
.Content
.ReadAsAsync<int>()
.Result;
}
}
My API signature is like below - with a breakpoint on I can see it is getting hit but test the parameter I am trying to pass is always null
[HttpPost]
[Route("api/MyController/SaveData")]
public HttpResponseMessage SaveData([FromBody]List<string> test)
{
try
{
//Rest of method removed for brevity
I have tried removing the [FromBody] Annotation from the WebAPI controller but test still is getting null with breakpoint in the SaveData API method
Try this:
private void Save(List<int> Ids)
{
var myAPI = ConfigurationManager.AppSettings["MyAPI"];
using (var client = new HttpClient())
{
var requestBody = JsonConvert.SerializeObject(Ids);
var postRequest = new StringContent(requestBody, Encoding.UTF8, "application/json");
var response = client.PostAsync(myAPI, postRequest).GetAwaiter().GetResult();
var rawResponse = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
// Do something with the answer
}
}
I also suggest to make the method private Task Save and replace .GetAwaiter().GetResult(); with await in front of that calls.
In my case i used System.Web.Http.ApiController instead of System.Web.Mvc.Controller. So over all code looks like
public class YourAppController : ApiController
{
[System.Web.Http.Route("publish-message")]
public HttpResponseMessage Post([System.Web.Http.FromBody] string msges)
{
//Your Code
return Request.CreateResponse(HttpStatusCode.OK, "");
}
}
public async Task<string> PublishMessageCall(string publishMessage){
var returnval = "";
string httpWebRqst = "http://localhost:543134535/publish-message";
using (HttpClient myClient = new HttpClient())
{
var jsonString = JsonConvert.SerializeObject(publishMessage);
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var response = await myClient.PostAsync(httpWebRqst, content);
var responseString = await response.Content.ReadAsStringAsync();
}
return await Task.FromResult(returnval);}
We are building a web application that consist of an Angular2 frontend, a ASP.NET Core web api public backend, and a ASP.NET Core web api private backend.
Uploading files from Angular2 to the public backend works. But we would prefer to post them forward to the private backend.
Current working code
[HttpPost]
public StatusCodeResult Post(IFormFile file)
{
...
}
From there I can save the file to disk using file.CopyTo(fileStream);
However, I want to re-send that file, or those files, or, ideally, the whole request to my second web api core.
I am not sure how to achieve this with the HttpClient class of asp.net core.
I've tried all kinds of things such as
StreamContent ss = new StreamContent(HttpContext.Request.Body);
var result = client.PostAsync("api/Values", ss).Result;
But my second backend gets an empty IFormFile.
I have a feeling it is possible to send the file(s) as a stream and reconstruct them on the other side, but can't get it to work.
The solution must use two web api core.
Solution
Public backend in DMZ
[HttpPost]
public StatusCodeResult Post(IFormFile file)
{
try
{
if (file != null && file.Length > 0)
{
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri(currentPrivateBackendAddress);
byte[] data;
using (var br = new BinaryReader(file.OpenReadStream()))
data = br.ReadBytes((int)file.OpenReadStream().Length);
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multiContent = new MultipartFormDataContent();
multiContent.Add(bytes, "file", file.FileName);
var result = client.PostAsync("api/Values", multiContent).Result;
return StatusCode((int)result.StatusCode); //201 Created the request has been fulfilled, resulting in the creation of a new resource.
}
catch (Exception)
{
return StatusCode(500); // 500 is generic server error
}
}
}
return StatusCode(400); // 400 is bad request
}
catch (Exception)
{
return StatusCode(500); // 500 is generic server error
}
}
Private backend
[HttpPost]
public void Post()
{
//Stream bodyStream = HttpContext.Request.Body;
if (Request.HasFormContentType)
{
var form = Request.Form;
foreach (var formFile in form.Files)
{
var targetDirectory = Path.Combine(_appEnvironment.WebRootPath, "uploads");
var fileName = GetFileName(formFile);
var savePath = Path.Combine(targetDirectory, fileName);
using (var fileStream = new FileStream(savePath, FileMode.Create))
{
formFile.CopyTo(fileStream);
}
}
}
}
Hi i had the same issue and this is what worked for me :
My setup is netCore MVC netCoreApi.
My MVC Controller looks like :
[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
Sp4RestClient dataPovider = new Sp4RestClient("http://localhost:60077/");
long size = files.Sum(f => f.Length);
foreach (var file in files)
{
await dataPovider.ImportFile(file);
}
return Ok();
}
DataProvider Method :
public async Task ImportFile(IFormFile file)
{
RestClient restClient = new RestClient(_queryBulder.BuildImportFileRequest());
using (var content = new MultipartFormDataContent())
{
content.Add(new StreamContent(file.OpenReadStream())
{
Headers =
{
ContentLength = file.Length,
ContentType = new MediaTypeHeaderValue(file.ContentType)
}
}, "File", "FileImport");
var response = await restClient.Post<IFormFile>(content);
}
}
And least my WebApi Controller :
[HttpPost]
[Route("ImportData")]
public IActionResult Import(IFormFile file)
{
return Ok();
}
To see the complete code here is my RestClient Post method :
public async Task<RestResult<T>> Post<T>(HttpContent content)
{
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage response = await httpClient.PostAsync(Endpoint, content);
if (response.StatusCode == HttpStatusCode.Created)
{
T result = JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
return new RestResult<T> { Result = result, ResultCode = HttpStatusCode.OK };
}
RestResult<T> nonOkResult =
new RestResult<T> { Result = default(T), ResultCode = response.StatusCode, Message = await response.Content.ReadAsStringAsync() };
return nonOkResult;
}
}
// Yeah i know im not getting HttpStatusCode.Created back ;)
happy coding ;)
API Code
[Route("api/upload/{id}")]
[HttpPost]
public async Task<IActionResult> Post(string id)
{
var filePath = #"D:\" + id; //+ Guid.NewGuid() + ".png";
if (Request.HasFormContentType)
{
var form = Request.Form;
foreach (var formFile in form.Files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
}
return Ok(new { Path = filePath });
}
Back End
[Route("home/UploadFile")]
[HttpPost]
public IActionResult UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return Content("file not selected");
var client = new HttpClient();
byte[] data;
using (var br = new BinaryReader(file.OpenReadStream()))
data = br.ReadBytes((int)file.OpenReadStream().Length);
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multiContent = new MultipartFormDataContent
{
{ bytes, "file", file.FileName }
};
var result = client.PostAsync("http://localhost:2821/api/upload/" + file.FileName, multiContent).Result;
return RedirectToAction("file");
}
Download Source
I was in a similar situation - I needed a proxy method for forwarding not only files but also JSON data and whatnot. I did not want to do any analysis of the data in my proxy to let the final receiver deal with it.
So with some help from #Anton Tykhyy I came to the following working solution:
byte[] arr = null;
using (var mems = new MemoryStream())
{
// read entire body into memory first because it might be chunked with unknown length
await request.Body.CopyToAsync(mems);
await mems.FlushAsync(); // not sure if needed after CopyToAsync - better safe then sorry
arr = mems.ToArray();
}
msg.Content = new ByteArrayContent(arr);
msg.Content.Headers.ContentLength = arr.Length;
// keep content-type header "as is" to preserve multipart boundaries etc.
msg.Content.Headers.TryAddWithoutValidation("Content-Type", request.ContentType);
var response = await _httpClient.SendAsync(msg);
I tested it with complex request that contained multipart form data with JSON field and multiple attached files, and all the data reached my backend server without any issues.
Ignoring the HttpClient when you call the private backend API, can you reference the private Core API project from the public Core API project and call the controller directly from the Core API project? See the request is still null/empty. If the request comes out with a value then the issue is with the use of the HttpClient.
Ideally, you want to create a package library(kind of SDK) for your private Core API that you want to distribute to consuming clients. This acts like a wrapper/proxy. This way you can isolate the private backend system and you can troubleshoot it in isolation. So you public Core API project(which is the private backend client) can reference it as nuget package.