HttpResponseMessage Send & Receive List or Array of <T> - .net-core

With the latest System.Net.Http.Json there are some methods that help with serializing / deserializing. I want to use the HttpResponseMessage Content hold my response Objects. They seem to store fine on the server but when I get them on the client side the Content is not what I was expecting. There is a lot of additional data injected and I am lost on how I can pass some data with additional meta data.
I am not sure what is going on.. If I try to use:
Content = new StringContent("test content")
on the client side I get "test content" but I get this:
Content:
{"version":"1.1","content":{"headers":[{"key":"Content-Type","value":["text/plain; charset=utf-8"]}]},"statusCode":200,"reasonPhrase":"Retrieved
Forecasts","headers":[],"trailingHeaders":[],"requestMessage":null,"isSuccessStatusCode":true}
Here is my Blazor code:
Client:
protected override async Task OnInitializedAsync()
{
try
{
//forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
HttpResponseMessage httpResponse = await Http.GetAsync("WeatherForecast");
if (httpResponse.IsSuccessStatusCode)
{
Console.WriteLine("Response Weather");
//forecasts = (await httpResponse.Content.ReadFromJsonAsync<WeatherForecast[]>()).ToList<WeatherForecast>();
Console.WriteLine("Content: " + await httpResponse.Content.ReadAsStringAsync());
//forecasts = Newtonsoft.Json.JsonConvert.DeserializeObject<WeatherForecast[]>(await httpResponse.Content.ReadAsStringAsync()).ToList<WeatherForecast>(); // Doesn't work
forecasts = await httpResponse.Content.ReadFromJsonAsync<WeatherForecast[]>(); //Doesn't work
}
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
Server:
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var rng = new Random();
WeatherForecast[] weather = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
HttpResponseMessage message = new HttpResponseMessage
{
StatusCode = OK,
ReasonPhrase = "Retrieved Weather",
//Content = new StringContent("test content")
//Content = JsonContent.Create<WeatherForecast[]>(weather)
//Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(weather))
};
string test = await message.Content.ReadAsStringAsync(); // This looks correct!
return message;
}

Related

StatusCode error 500 when sending a POST resquest in XF

I'm writing an API in EF to send FCM notifications:
[HttpPost]
public void PushNotificationToFCM(string deviceTokens, string title, string body, object data, string linkdirection)
{
...
// This registration token comes from the client FCM SDKs.
var registrationToken = deviceTokens;
// See documentation on defining a message payload.
var message = new Message()
{
Apns = new ApnsConfig { Aps = new Aps { ContentAvailable = true, Sound = "default" } },
Data = new Dictionary<string, string>()
{
{ "link", linkdirection },
},
Token = registrationToken,
Notification= new FirebaseAdmin.Messaging.Notification()
{
Title = title,
Body = body,
}
};
// Send a message to the device corresponding to the provided
// registration token.
string response = FirebaseMessaging.DefaultInstance.SendAsync(message).Result;
// Response is a message ID string.
Debug.WriteLine("Successfully sent message: " + response);
}
I tried on Postman or Swagger everything works fine.
I proceed to write the send command in XF:
.xaml.cs
protected async void SendFirebase()
{
string devicetoken = listDeviceID.deviceidphone;
string titlefirebase = "Title";
string bodyfirebase = "Description";
string linkfirebase = "https://applink/..";
await _apiService.AddNotifyFirebase(devicetoken, titlefirebase, bodyfirebase, linkfirebase);
}
public async Task AddNotifyFirebase(string devicetoken, string titlefirebase, string bodyfirebase, string linkfirebase)
{
var model = new
{
devicetoken = devicetoken,
titlefirebase = titlefirebase,
bodyfirebase = bodyfirebase,
linkfirebase = linkfirebase,
};
var json = JsonConvert.SerializeObject(model);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Post, "https://linkapi/api/SendNotifyDeviceFirebase");
request.Content = content;
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync();
}
}
However I get the error: StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.NSUrlSessionHandler+NSUrlSessionDataTaskStreamContent, ....
Where did I go wrong? Looking forward to everyone's help. Thank you!
Update
I check on Swagger:
Everything seems fine!
I then changed the send command back:
public async Task AddNotifyFirebase(string devicetoken, string titlefirebase, string bodyfirebase, string linkfirebase)
{
var request = new HttpRequestMessage(HttpMethod.Post, "https://xxxxxx/api/SendNotifyDeviceFirebase?deviceTokens=" + devicetoken + "&title=" + titlefirebase + "&body=" + bodyfirebase + "&linkdirection=" + linkfirebase);
//request.Headers.Add("Accept-Encoding", "gzip");
//request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain; charset=utf-8");
HttpResponseMessage response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync();
}
}
I get the error again: StatusCode: 415, ReasonPhrase: 'Unsupported Media Type'
I have handled the problem. Thank you all
public async Task AddNotifyFirebase(NotifyFireBase NotifyFireBaseAdd)
{
var urlput = "......";
HttpClient client = new HttpClient();
var content = NotifyFireBaseAdd;
var stringContent = new StringContent(JsonConvert.SerializeObject(content), Encoding.UTF8, "application/json");
await client.PostAsync(urlput, stringContent);
}

HttpClient return json response on localhost when application live on server it's return html

I'm calling API using HttpClient below code running fine on localhost but when it's live on server it's return html response.
here is the code i'm using
public async Task<ActionResult> getPost()
{
string url = "https://www.instagram.com/p/Bg9DwFYHARK/?__a=1";
HttpClient _client = new HttpClient();
var Response = await _client.GetAsync(url);
if (Response.IsSuccessStatusCode)
{
var Result = Response.Content.ReadAsStringAsync().Result;
//here in **Result** i've received html instead of json or when i received html instead
of json got error on DeserializeObject.
var Jsonresult = JsonConvert.DeserializeObject<post>(Result);
return Json(new { isValid = false, error = "post found", obj = Jsonresult }, JsonRequestBehavior.AllowGet);
}
else
{
return Json(new { isValid = false, error = "post not found" }, JsonRequestBehavior.AllowGet);
}
}
This method response is JSON.
string uri = "https://www.instagram.com/p/Bg9DwFYHARK/?__a=1";
private static async Task<ActionResult> getPost(string uri)
{
var webRequest = WebRequest.Create(uri) as HttpWebRequest;
if (webRequest == null)
{
return;
}
webRequest.ContentType = "application/json";
webRequest.UserAgent = "Nothing";
using (var s = webRequest.GetResponse().GetResponseStream())
{
using (var sr = new StreamReader(s))
{
// var contributorsAsJson = sr.ReadToEnd();
var contributors = JsonConvert.DeserializeObject<Create a list >(contributorsAsJson);
// contributors.ForEach(Console.WriteLine);
return contributors;
}
}
}
I get out put is:
{"graphql":{"shortcode_media":{"__typename":"GraphImage","id":"1746568728937235530","shortcode":"Bg9DwFYHARK","dimensions":{"height":1080,"width":1080},"gating_info":null,"fact_check_overall_rating":null,"fact_check_information":null,"sensitivity_friction_info":null,"media_overlay_info":null,"media_preview":"ACoqytpjBOM+tQs6t07nvxwOn4889q2TDuGKzrfT2kkKtkBDz7j2PT0/OlpuUm7cvQqrbSS/cBb6D/IqI8DHfvXZiVI12qpBA+7xwPr0/WueMXm3LMBx94jryf0PrSTdwaVrlSKMsKd5ZrYji7n8B/8AWp3k+wp3ILSKT0FBkW2YM/AcEZ9xz+o4+uBVgyBSB26VDcWgnYFuVUEbfr3B9Qarl0HcRrgqQWVlX5snjIBxgsOw9fTFR2xEu+QdHbj6DgH8earPayvuUuXXHTGC2OgZv5461pQRGKNY+PlABx69/wBamKvuVJ9iMgikqwy4qPZSasJambblpm3dlP5n69x/9atgetZ1h/qV/H+ZrQHQfhWl7tk2JqY7bQT6UtRvTAN2aZUSnn8am2L6D8qdk9xbH//Z","display_url":"https://instagram.fcok6-1.fna.fbcdn.net/v/t51.2885-15/e35/29400587_173904939930435_4131401846013034496_n.jpg?_nc_ht=instagram.fcok6-1.fna.fbcdn.net&_nc_cat=103&_nc_ohc=cgtRDraKPGoAX_a9AvI&oh=fc8640215bbe4a5003bbaf694113951c&oe=5F2FFE5A","display_resources":[{"src":"https://instagram.fcok6-1.fna.fbcdn.net/v/t51.2885-15/sh0.08/e35/s640x640/29400587_173904939930435_4131401846013034496_n.jpg?_nc_ht=instagram.fcok6-1.fna.fbcdn.net&_nc_cat=103&_nc_ohc=cgtRDraKPGoAX_a9AvI&oh=e971123fdc4eb19abe4a96b35195dd9f&oe=5F30CFBD","config_width":640,"config_height":640},{"src":"https://instagram.fcok6-1.fna.fbcdn.net/v/t51.2885-15/sh0.08/e35/s750x750/29400587_173904939930435_4131401846013034496_n.jpg?_nc_ht=instagram.fcok6-1.fna.fbcdn.net&_nc_cat=103&_nc_ohc=cgtRDraKPGoAX_a9AvI&oh=33fe7c336ecff4205e161452adfc7ecf&oe=5F31703D","config_width":750,"config_height":750},{"src":"https://instagram.fcok6-1.fna.fbcdn.net/v/t51.2885-15/e35/29400587_173904939930435_4131401846013034496_n.jpg?_nc_ht=instagram.fcok6-1.fna.fbcdn.net&_nc_cat=103&_nc_ohc=cgtRDraKPGoAX_a9AvI&oh=fc8640215bbe4a5003bbaf694113951c&oe=5F2FFE5A","config_width":1080,"config_height":1080}],"accessibility_caption":"Photo by Ateeq Khalil in Salt N Pepper Liberty. Image may contain: 1 person","is_video":false,"tracking_token":"eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjp0cnVlLCJ1dWlkIjoiZWQ2ZjUxNTZjMjc5NDU5MzlmMmQ1MTQzZDBhZmIyYjYxNzQ2NTY4NzI4OTM3MjM1NTMwIn0sInNpZ25hdHVyZSI6IiJ9","edge_media_to_tagged_user":{"edges":[]},"edge_media_to_caption":{"edges":[]},"caption_is_edited":false,"has_ranked_comments":false,"edge_media_to_parent_comment":{"count":0,"page_info":{"has_next_page":false,"end_cursor":null},"edges":[]},"edge_media_to_hoisted_comment":{"edges":[]},"edge_media_preview_comment":{"count":0,"edges":[]},"comments_disabled":false,"commenting_disabled_for_viewer":false,"taken_at_timestamp":1522427239,"edge_media_preview_like":{"count":19,"edges":[]},"edge_media_to_sponsor_user":{"edges":[]},"location":{"id":"1730525857251432","has_public_page":true,"name":"Salt N Pepper Liberty","slug":"salt-n-pepper-liberty","address_json":"{\"street_address\": \"48 Commercial Zone Liberty Market, Gulberg III\\u060c\", \"zip_code\": \"\", \"city_name\": \"Lahore, Pakistan\", \"region_name\": \"\", \"country_code\": \"PK\", \"exact_city_match\": false, \"exact_region_match\": false, \"exact_country_match\": false}"},"viewer_has_liked":false,"viewer_has_saved":false,"viewer_has_saved_to_collection":false,"viewer_in_photo_of_you":false,"viewer_can_reshare":true,"owner":{"id":"5394358177","is_verified":false,"profile_pic_url":"https://instagram.fcok6-1.fna.fbcdn.net/v/t51.2885-19/s150x150/29402856_149619235868663_6235876322871083008_n.jpg?_nc_ht=instagram.fcok6-1.fna.fbcdn.net&_nc_ohc=gSyry9Wj5UkAX_bQlzD&oh=bdc6a6b82077c8e461aedbaffa7e49db&oe=5F32308B","username":"ateeq.khalil","blocked_by_viewer":false,"restricted_by_viewer":null,"followed_by_viewer":false,"full_name":"Ateeq Khalil","has_blocked_viewer":false,"is_private":false,"is_unpublished":false,"requested_by_viewer":false,"edge_owner_to_timeline_media":{"count":7},"edge_followed_by":{"count":175}},"is_ad":false,"edge_web_media_to_related_media":{"edges":[]},"edge_related_profiles":{"edges":[]}}}}

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object

After making an api call, if i input a wrong detail. My app keeps breaking with a null exception
I tried using the if-else to solve it. but it is still the same error
public class RemoteService
{
HttpClient httpClient;
public RemoteService()
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri($"{App.BackendUrl}/");
}
public async Task<WeatherResponse> GetWeatherData(string query)
{
var weatherResponse = new WeatherResponse();
var response = await httpClient.GetAsync($"weather?q=" + query + App.AppID);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
weatherResponse = JsonConvert.DeserializeObject<WeatherResponse>(content);
weatherResponse.Error = false;
return weatherResponse;
}
else
{
//await Application.Current.MainPage.DisplayAlert("Error", "City not found", "OK");
return new WeatherResponse { Error = true };
}
}
}
The problem was actually from the viewmodel class. Solved

Read Asp.Net Core Response body in ActionFilterAttribute

I'm using Asp.Net Core as a Rest Api Service.
I need access to request and response in ActionFilter. Actually, I found the request in OnActionExcecuted but I can't read the response result.
I'm trying to return value as follow:
[HttpGet]
[ProducesResponseType(typeof(ResponseType), (int)HttpStatusCode.OK)]
[Route("[action]")]
public async Task<IActionResult> Get(CancellationToken cancellationToken)
{
var model = await _responseServices.Get(cancellationToken);
return Ok(model);
}
And in ActionFilter OnExcecuted method as follow:
_request = context.HttpContext.Request.ReadAsString().Result;
_response = context.HttpContext.Response.ReadAsString().Result; //?
I'm trying to get the response in ReadAsString as an Extension method as follow:
public static async Task<string> ReadAsString(this HttpResponse response)
{
var initialBody = response.Body;
var buffer = new byte[Convert.ToInt32(response.ContentLength)];
await response.Body.ReadAsync(buffer, 0, buffer.Length);
var body = Encoding.UTF8.GetString(buffer);
response.Body = initialBody;
return body;
}
But, there is no result!
How I can get the response in OnActionExcecuted?
Thanks, everyone for taking the time to try and help explain
If you're logging for json result/ view result , you don't need to read the whole response stream. Simply serialize the context.Result:
public class MyFilterAttribute : ActionFilterAttribute
{
private ILogger<MyFilterAttribute> logger;
public MyFilterAttribute(ILogger<MyFilterAttribute> logger){
this.logger = logger;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
var result = context.Result;
if (result is JsonResult json)
{
var x = json.Value;
var status = json.StatusCode;
this.logger.LogInformation(JsonConvert.SerializeObject(x));
}
if(result is ViewResult view){
// I think it's better to log ViewData instead of the finally rendered template string
var status = view.StatusCode;
var x = view.ViewData;
var name = view.ViewName;
this.logger.LogInformation(JsonConvert.SerializeObject(x));
}
else{
this.logger.LogInformation("...");
}
}
I know there is already an answer but I want to also add that the problem is the MVC pipeline has not populated the Response.Body when running an ActionFilter so you cannot access it. The Response.Body is populated by the MVC middleware.
If you want to read Response.Body then you need to create your own custom middleware to intercept the call when the Response object has been populated. There are numerous websites that can show you how to do this. One example is here.
As discussed in the other answer, if you want to do it in an ActionFilter you can use the context.Result to access the information.
For logging whole request and response in the ASP.NET Core filter pipeline you can use Result filter attribute
public class LogRequestResponseAttribute : TypeFilterAttribute
{
public LogRequestResponseAttribute() : base(typeof(LogRequestResponseImplementation)) { }
private class LogRequestResponseImplementation : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var requestHeadersText = CommonLoggingTools.SerializeHeaders(context.HttpContext.Request.Headers);
Log.Information("requestHeaders: " + requestHeadersText);
var requestBodyText = await CommonLoggingTools.FormatRequestBody(context.HttpContext.Request);
Log.Information("requestBody: " + requestBodyText);
await next();
var responseHeadersText = CommonLoggingTools.SerializeHeaders(context.HttpContext.Response.Headers);
Log.Information("responseHeaders: " + responseHeadersText);
var responseBodyText = await CommonLoggingTools.FormatResponseBody(context.HttpContext.Response);
Log.Information("responseBody: " + responseBodyText);
}
}
}
In Startup.cs add
app.UseMiddleware<ResponseRewindMiddleware>();
services.AddScoped<LogRequestResponseAttribute>();
Somewhere add static class
public static class CommonLoggingTools
{
public static async Task<string> FormatRequestBody(HttpRequest request)
{
//This line allows us to set the reader for the request back at the beginning of its stream.
request.EnableRewind();
//We now need to read the request stream. First, we create a new byte[] with the same length as the request stream...
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
//...Then we copy the entire request stream into the new buffer.
await request.Body.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
//We convert the byte[] into a string using UTF8 encoding...
var bodyAsText = Encoding.UTF8.GetString(buffer);
//..and finally, assign the read body back to the request body, which is allowed because of EnableRewind()
request.Body.Position = 0;
return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}
public static async Task<string> FormatResponseBody(HttpResponse response)
{
//We need to read the response stream from the beginning...
response.Body.Seek(0, SeekOrigin.Begin);
//...and copy it into a string
string text = await new StreamReader(response.Body).ReadToEndAsync();
//We need to reset the reader for the response so that the client can read it.
response.Body.Seek(0, SeekOrigin.Begin);
response.Body.Position = 0;
//Return the string for the response, including the status code (e.g. 200, 404, 401, etc.)
return $"{response.StatusCode}: {text}";
}
public static string SerializeHeaders(IHeaderDictionary headers)
{
var dict = new Dictionary<string, string>();
foreach (var item in headers.ToList())
{
//if (item.Value != null)
//{
var header = string.Empty;
foreach (var value in item.Value)
{
header += value + " ";
}
// Trim the trailing space and add item to the dictionary
header = header.TrimEnd(" ".ToCharArray());
dict.Add(item.Key, header);
//}
}
return JsonConvert.SerializeObject(dict, Formatting.Indented);
}
}
public class ResponseRewindMiddleware {
private readonly RequestDelegate next;
public ResponseRewindMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
Stream originalBody = context.Response.Body;
try {
using (var memStream = new MemoryStream()) {
context.Response.Body = memStream;
await next(context);
//memStream.Position = 0;
//string responseBody = new StreamReader(memStream).ReadToEnd();
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
} finally {
context.Response.Body = originalBody;
}
}
You can also do...
string response = "Hello";
if (result is ObjectResult objectResult)
{
var status = objectResult.StatusCode;
var value = objectResult.Value;
var stringResult = objectResult.ToString();
responce = (JsonConvert.SerializeObject(value));
}
I used this in a .net core app.
Hope it helps.

'Server side events' send with the ASP Web Api do not arrive?

I created a test source which should send a message to the client every x time. This is the ApiController:
public class TestSourceController : ApiController
{
private static readonly ConcurrentQueue<StreamWriter> ConnectedClients = new ConcurrentQueue<StreamWriter>();
[AllowAnonymous]
[Route("api/sources/test")]
public HttpResponseMessage Get()
{
var response = Request.CreateResponse();
response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>) OnStreamAvailable,
"text/event-stream");
return response;
}
private static void OnStreamAvailable(Stream stream, HttpContent headers, TransportContext context)
{
var clientStream = new StreamWriter(stream);
ConnectedClients.Enqueue(clientStream);
}
private static void DoThings()
{
const string outboundMessage = "Test";
foreach (var clientStream in ConnectedClients)
{
clientStream.WriteLine("data:" + JsonConvert.SerializeObject(outboundMessage));
clientStream.Flush();
}
}
}
The clientStream.Flush(); is called like expected and without exceptions.
I handle it in AngularJS like this:
$scope.handleServerCallback = function (data) {
console.log(data);
$scope.$apply(function() {
$scope.serverData = data;
});
};
$scope.listen = function () {
$scope.eventSource = new window.EventSource("http://localhost:18270/api/sources/test");
$scope.eventSource.onmessage = $scope.handleServerCallback;
$scope.eventSource.onopen = function() { console.log("Opened source"); };
$scope.eventSource.onerror = function (e) { console.error(e); };
};
$scope.listen();
My guess is it's a problem with the server since I can see the "EventStream" from the test call is empty in the chrome debugger.
Does anyone know how to make sure the messages arrive at the client?
The solution was quite easy, according to the spec every line has to end with "\n" and the very last line with "\n\n".
So:
clientStream.WriteLine("data:" + JsonConvert.SerializeObject(outboundMessage) + "\n\n");
Solves it.

Resources