Problems with Newtonsoft serialization in project hosted on IIS - asp.net

I am working on a project built using ASP.Net Web API 2 and AngularJS. Everything works as expected when I run the project from Visual Studio. But after deploying the project in Local IIS server the API calls giving me this error
"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'
YES I have the following line in my WebAPIConfig file
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
And here's an example API method I am using
// GET api/PatientsAPI
[ResponseType(typeof(Patient))]
public IHttpActionResult GetPatients()
{
var patients = from patient in db.Patients
select new
{
id = patient.id,
name = patient.name,
contact = patient.contact
};
return Ok(patients);
}

Related

How to retrieve JSON data from HttpContent

I'm buildin a console Web API to communicate with a localhost server, hosting computer games and highscores for them. Every time I run my code, I get this charming error:
fail:
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NotSupportedException: Deserialization of types without a
parameterless constructor, a singular parameterized constructor, or a
parameterized constructor annotated with 'JsonConstructorAttribute' is
not supported. Type 'System.Net.Http.HttpContent'. Path: $ |
LineNumber: 0 | BytePositionInLine: 1.
This is the method I'm using to post to the database. Note that this method is not in the console application. It is in the ASP.NET Core MvC application opening a web browser and listening for HTTP requests (which can come from the console application).
[HttpPost]
public ActionResult CreateHighscore(HttpContent requestContent)
{
string jasonHs = requestContent.ReadAsStringAsync().Result;
HighscoreDto highscoreDto = JsonConvert.DeserializeObject<HighscoreDto>(jasonHs);
var highscore = new Highscore()
{
Player = highscoreDto.Player,
DayAchieved = highscoreDto.DayAchieved,
Score = highscoreDto.Score,
GameId = highscoreDto.GameId
};
context.Highscores.Add(highscore);
context.SaveChanges();
return NoContent();
}
I'm sending POST requests in a pure C# console application, with information gathered from user input, but the result is exactly the same when using Postman for post requests - the above NotSupportedException.
private static void AddHighscore(Highscore highscore)
{
var jasonHighscore = JsonConvert.SerializeObject(highscore);
Uri uri = new Uri($"{httpClient.BaseAddress}highscores");
HttpContent requestContent = new StringContent(jasonHighscore, Encoding.UTF8, "application/json");
var response = httpClient.PostAsync(uri, requestContent);
if (response.IsCompletedSuccessfully)
{
OutputManager.ShowMessageToUser("Highscore Created");
}
else
{
OutputManager.ShowMessageToUser("Something went wrong");
}
}
I'm new to all this HTTP requests stuff, so if you spot some glaring errors in my code, that would be appreciated. Though, the most important question is, what am I missing, and how can I read from the HttpContent object, to be able to create a Highscore object to send to the database?
It seems to be the string jasonHs... line that is the problem, since the app crashed in exactly the same way, when I commented out the rest of the ActionResult method.
Based on your code, we can find that you make a HTTP Post request with a json string data (serialized from a Highscore object) from your console client to Web API backend.
And in your action method, you create an instance of Highscore manually based on received data, so why not make your action accept a Highscore type parameter, like below. Then the model binding system would help bind data to action parameter(s) automatically.
[HttpPost]
public ActionResult CreateHighscore([FromBody]Highscore highscore)
{
//...

Uploading File (IfromFile) via HttpClient To webApi

Need help. I am trying to save or uploud a file (IFormFile) from a Project Web to the Web Api, consuming the web api via httpClient. I am getting the following error: System.NotSupportedException: The collection type 'Microsoft.AspNetCore.Http.IHeaderDictionary' on 'Microsoft.AspNetCore.Http.IFormFile.Headers' is not supported.enter image description here
enter image description here
NotSupportedException: The collection type 'Microsoft.AspNetCore.Http.IHeaderDictionary' on 'Microsoft.AspNetCore.Http.IFormFile.Headers' is not supported.
It seems that you are serializing a FormFile, which cause the above issue.
I am trying to save or uploud a file (IFormFile) from a Project Web to the Web Api, consuming the web api via httpClient.
public async Task<IActionResult> Online([FromForm]CandidaturaAddModel model)
{
var formContent = new MultipartFormDataContent();
formContent.Add(new StringContent(model.Senha), "Senha");
formContent.Add(new StringContent(System.Text.Json.JsonSerializer.Serialize(model.AnoLectvo)), "AnoLectvo");
//...
//for other properties, such as Email, Genero etc
//...
formContent.Add(new StreamContent(model.Foto.OpenReadStream()), "Foto", Path.GetFileName(model.Foto.FileName));
_httpClient.BaseAddress = new Uri("https://localhost:xxxx/");
var response = await _httpClient.PostAsync("/api/xxx/CandidaturaAdd", formContent);
if (response.IsSuccessStatusCode)
{
//....
}
Test Result

.net framework 4.7.2 broke httpclient

My ASP.NET MVC project used HttpClient.PostAsync() to post to a webAPI endpoint. All is fine until I recently upgraded the project to target .NET Framework 4.7.2. Now the call will end with HTTP Status 405 (Method not allowed). However, endpoints with [HTTPGet] work. In a desperate measure, I set the API controller with [EnableCors(origins: "", headers: "", methods: "*")] and call the endpoint with the same domain and port (localhost to localhost). I even reverted the project to 4.6.1. It still throws 405 error. Could you help? Thank you.
public static async Task < T > Post < T > (string baseUrl, string urlSegment, HttpContent postContent) {
string responseContent = string.Empty;
Uri returnUrl = null;
using(HttpClient client = GetClient(baseUrl)) {
HttpResponseMessage response = await client.PostAsync(urlSegment.TrimStart('/'), postContent).ConfigureAwait(false);
if (response.IsSuccessStatusCode) {
returnUrl = response.Headers.Location;
}
return JsonConvert.DeserializeObject < T > (returnUrl.ToString());
}
}
It turned out that the WebAPI endpoint path has been changed. In addition, I reverted the GIT commit to rollback to a version before I upgrade to DotNet Framework 4.7.2, then cherry-picked all the commits after the 4.7.2 upgrade.

HttpClient, UseDefaultCredentials, Windows Authentication, .NET Core 2.0+ console application receives 401 Unauthorized

I am trying to make an HTTP GET call from a .NET Core 3.0 console application. The endpoint expects Windows Authentication. The endpoint is an in-production .NET Framework 4.5.2 Web API service on IIS successfully used by several client applications.
My test program succeeds for a net45 framework target. In contrast, the netcoreapp3.0 build receives a 401 Unauthorized.
Here is my method:
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
static async Task<string> Get(string serverUri, string requestUri)
{
var handler = new HttpClientHandler() { UseDefaultCredentials = true };
var client = new HttpClient(handler) { BaseAddress = new Uri(serverUri) };
return await client.GetStringAsync(requestUri);
}
Here is my project file.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net45;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<PackageReference Include="System.Net.Http" Version="4.3" />
</ItemGroup>
</Project>
Running .\bin\Debug\net45\HttpClientTest.exe returns the expected JSON result.
Running dotnet .\bin\Debug\netcoreapp3.0\HttpClientTest.dll receives a 401 Unauthorized.
Unhandled exception. System.AggregateException: One or more errors occurred. (Response status code does not indicate success: 401 (Unauthorized).)
---> System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at System.Net.Http.HttpClient.GetStringAsyncCore(Task`1 getTask)
at ConsoleApp1.Program.Get(String serverUri, String requestUri) in C:\temp\coreapp3\Program.cs:line 16
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task`1.get_Result()
at ConsoleApp1.Program.Main(String[] args) in C:\temp\coreapp3\Program.cs:line 22
How do I fix this?
I have also tried the variations below, without any change in output:
handler.Credentials = CredentialCache.DefaultNetworkCredentials;
handler.Credentials = CredentialCache.DefaultCredentials;
handler.Credentials = new NetworkCredential(username, password, domain);
This workaround works for me add this before creating HttpClient / Handler:
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
So we are running into the same issue with HttpClient, but on the first request it is unauthorized, but if we do a another quick request call, it goes through just fine, and we get authorized.
If I do this on a .NET 4.7.1 console app, it works perfect. If I do it in a .NET 4.7.1 empty web app, it works fine, only if I do .NET Core 2.x do we get this issue.
var result = string.Empty;
var responseMessage = _client.GetAsync(url).Result;
if (responseMessage.IsSuccessStatusCode)
{
result = await responseMessage.Content.ReadAsStringAsync();
}
else
{
var responseMessage2 = _client.GetAsync(url).Result;
if (responseMessage2.IsSuccessStatusCode)
{
result = await responseMessage2.Content.ReadAsStringAsync();
}
}
return result;

Assembly.UnsafeLoadFrom causes web app crash

I am trying to load a DLL from internet, more specifically it is Azure storage (Blob), so I used "Assembly.UnsafeLoadFrom" like this:
Assembly.UnsafeLoadFrom(#"https://accountname.blob.core.windows.net/test/calculator.dll");
But becuaset this specific call, my web app (published) returns:
"The specified CGI application encountered an error and the server
terminated the process."
The weird part is if I am using my local build, it is fine. there is no crash and the return result is correct.
I am using Visual Studio 2015 and .net 5.
Please let me know how to resolve this issue or how to debug it.
Thanks
For a simple way, you could achieve your purpose by the following code:
calculator.dll
public class Calculator
{
public string HelloWorld(string userName)
{
return string.Format("Hello world, {0}!", userName);
}
}
HomeController.cs
public async Task<ActionResult> Index()
{
string url = "https://brucechen.blob.core.windows.net/dll/calculator.dll";
HttpClient client = new HttpClient();
var bytes = await client.GetByteArrayAsync(url);
//load assembly from bytes
Assembly assembly = Assembly.Load(bytes);
var calc = assembly.CreateInstance("calculator.Calculator");
//invoke the method and get result
var result = calc.GetType().InvokeMember("HelloWorld", BindingFlags.InvokeMethod, null, calc, new[] { "Bruce" });
ViewData["result"] = result;
return View();
}
Result

Resources