C# How a winform capture server sent event - c#

How to subscribe a desktop client to server sent event. so whenever something will be pushed from server side my winform application should be able to capture and show that message. just a looking for a example code. i got some relevant links.
Here i am highlighting a sample code where a javascript side and ASP.Net MVC talking to each other by server sent event.
<input type="text" id="userid" placeholder="UserID" /><br />
<input type="button" id="ping" value="Ping" />
<script>
var es = new EventSource('/home/message');
es.onmessage = function (e) {
console.log(e.data);
};
es.onerror = function () {
console.log(arguments);
};
$(function () {
$('#ping').on('click', function () {
$.post('/home/ping', {
UserID: $('#userid').val() || 0
});
});
});
</script>
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Web.Mvc;
using Newtonsoft.Json;
namespace EventSourceTest2.Controllers {
public class PingData {
public int UserID { get; set; }
public DateTime Date { get; set; } = DateTime.Now;
}
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();
public void Ping(int userID) {
pings.Enqueue(new PingData { UserID = userID });
}
public void Message() {
Response.ContentType = "text/event-stream";
do {
PingData nextPing;
if (pings.TryDequeue(out nextPing)) {
Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n");
}
Response.Flush();
Thread.Sleep(1000);
} while (true);
}
}
}
i want to communicate between a web api and winform by server sent event. so please show me how a winform can communicate subscribe to server sent event ?
if any small example would be added....will be great help. thanks

I wrote a basic implementation using the TcpClient.
Variables:
Host: 127.0.0.1
Port: 5000
Method: GET
Url: /home/message
Listener
private static async Task ConnectEventStreamAsync(CancellationToken token)
{
var client = new TcpClient();
try
{
await client.ConnectAsync("127.0.0.1", 5000);
if (!client.Connected)
throw new Exception("Unable to connect the host");
var encoding = Encoding.UTF8;
var stream = client.GetStream();
var connectBytes = encoding.GetBytes(
"GET /home/message HTTP/1.1\r\n" +
"Host: 127.0.0.1\r\n" +
"Content-Length: 0\r\n\r\n"
);
await stream.WriteAsync(connectBytes, 0, connectBytes.Length, token);
var buffer = new byte[4096];
while (!token.IsCancellationRequested)
{
var readCount = await stream.ReadAsync(buffer, 0, buffer.Length, token);
if (readCount > 0)
{
var message = encoding.GetString(buffer, 0, readCount);
using (var stringReader = new StringReader(message))
{
string line;
while ((line = await stringReader.ReadLineAsync()) != null)
{
//--try to read the next chunk
if (!int.TryParse(line, NumberStyles.HexNumber, null, out var bytes))
continue;
var data = await stringReader.ReadLineAsync();
Console.WriteLine($">>New Event ({bytes} bytes)\r\n{data}");
}
}
}
await Task.Delay(500, token);
}
}
finally
{
client.Dispose();
}
}
EDIT: Here is other solution using the HttpClient. I added more code to parse the text-eventstream messages. You can read more here to make improvements.
private static async Task ConnectEventStreamAsync(CancellationToken token)
{
var client = new HttpClient
{
Timeout = Timeout.InfiniteTimeSpan
};
try
{
var response = await client.GetAsync(
"http://127.0.0.1:5000/home/message",
HttpCompletionOption.ResponseHeadersRead,
token
);
if (!response.IsSuccessStatusCode)
throw new Exception("Unable to connect the stream");
var isTextEventStream = response.Content.Headers.ContentType.MediaType == "text/event-stream";
if (!isTextEventStream)
throw new InvalidOperationException("Invalid resource content type");
var stream = await response.Content.ReadAsStreamAsync();
var buffer = new byte[4096];
while (!token.IsCancellationRequested)
{
var readCount = await stream.ReadAsync(buffer, 0, buffer.Length, token);
if (readCount > 0)
{
var data = Encoding.UTF8.GetString(buffer, 0, readCount);
await ParseDataAsync(data);
}
await Task.Delay(500, token);
}
}
finally
{
client.Dispose();
}
async Task ParseDataAsync(string data)
{
using (var stringReader = new StringReader(data))
{
string line;
while ((line = await stringReader.ReadLineAsync()) != null)
{
if (line.StartsWith("event:"))
{
var eventText = line.Substring("event:".Length);
Console.WriteLine($">>Event ({eventText.Length} bytes)\r\n{eventText}");
continue;
}
if (line.StartsWith("data:"))
{
var dataText = line.Substring("data:".Length);
Console.WriteLine($">>Data ({dataText.Length} bytes)\r\n{dataText}");
continue;
}
if (line.StartsWith("id:"))
{
var eventId = line.Substring("id:".Length);
Console.WriteLine($">>Event ID ({eventId.Length} bytes)\r\n{eventId}");
continue;
}
if (line.StartsWith("retry:"))
{
var retryValue = line.Substring("retry:".Length);
Console.WriteLine($">>Retry ({retryValue.Length} bytes)\r\n{retryValue}");
continue;
}
if (line.StartsWith(":"))
{
Console.WriteLine($">>Comment ({line.Length - 1} bytes)\r\n{line.Substring(1)}");
}
}
}
}
}
The server part using Asp.Net Core (very similar to the one displayed by the user who asks).
...
private static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();
[HttpGet]
public void Ping(int userID)
{
pings.Enqueue(new PingData { UserID = userID });
}
[HttpGet]
public void Message()
{
Response.ContentType = "text/event-stream";
Response.WriteAsync($":Hello {Request.Host}\n");
const int intervalMs = 1000;
do
{
if (pings.TryDequeue(out var nextPing))
{
Response.WriteAsync($"event:Ping\n");
Response.WriteAsync($"retry:{intervalMs}\n");
Response.WriteAsync($"id:{DateTime.Now.Ticks}\n");
Response.WriteAsync($"data:{JsonConvert.SerializeObject(nextPing)}\n\n");
}
Thread.Sleep(intervalMs);
} while (Response.Body.CanWrite);
}
public class PingData
{
public int UserID { get; set; }
public DateTime Date { get; set; } = DateTime.Now;
}
...

Related

How to download file from WebApi in ActionResult Asp.net Mvc C#

A method called GetFile is written to a WebApi project that returns HttpResponseMessage:
WebApi Controller I am using NReco.PdfGenerated library
[HttpGet]
[Route("GetFile")]
[NoCache]
public HttpResponseMessage GetFile()
{
try
{
var httpRequest = HttpContext.Current.Request;
var html = HttpUtility.UrlDecode(httpRequest.Headers["_GetFile"] ?? "");
if (string.IsNullOrWhiteSpace(html))
{
return null;
}
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(new MemoryStream(new HtmlToPdfConverter().GeneratePdf(html)))
};
response.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
return response;
}
catch
{
}
return null;
}
In another project, I want to connect to the GetFile method via ActionResult and get that file. ActionResult is written as follows:
Request Class:
public class GeneratedHtmlToPdfRequest
{
[AllowHtml]
public string Html { get; set; }
}
Controller (Asp.net Mvc):
[HttpPost]
public ActionResult GeneratedHtmlToPdf(GeneratedHtmlToPdfRequest request)
{
var userData = CookieController.GetUserDataCookie(CookieController.SGP_PORTAL_ALL_USER);
string encodeText = HttpUtility.UrlEncode(request.Html);
var response = var response = WebController.CallApiWithHeader(
"http://baseUrlWebApi.com" , "GetFile",
"_GetFile",
encodeText).Result; //call web api method
var result = response.ReadAsByteArrayAsync().Result;
TempData[WebVariables.TEMP_DATA_FILE] = result;
return Json(new PostGeneratedHtmlToPdf()
{
RedirectUrl = WebController.GetCurrentWebsiteRoot() + "***/***/FileDownload/" + DateTime.Now.ToString("yyyyMMddHHmmss")
});
}
[HttpGet]
public virtual ActionResult FileDownload(string id)
{
var tempByte = (byte[]) TempData[WebVariables.TEMP_DATA_FILE];
TempData[WebVariables.TEMP_DATA_FILE] = tempByte;
return File(tempByte , "application/pdf" , id);
}
Function (Call web api)
public static async Task<HttpContent> CallApiWithHeader(string url ,string methodName , string headerName = "", string header = "")
{
try
{
HttpClient client = new HttpClient {BaseAddress = new Uri(url)};
client.DefaultRequestHeaders.Add(headerName , header);
return client.GetAsync(methodName).Result.Content;
}
catch (Exception ex)
{
return null;
}
}
jquery is written that calls the GeneratedHtmlToPdf method:
window.$('body').on('click',
'#bDownload',
function(event) {
event.preventDefault();
var html = window.$('#layoutLegalBill').html();
window.$('#layoutLegalBill').html(ShowLayoutLoading());
const formData = new FormData();
formData.append('request.Html', html);
var xmlHttpRequest = new XMLHttpRequest();
if (!window.XMLHttpRequest) {
xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlHttpRequest.open(
"POST",
'#Url.Action("GeneratedHtmlToPdf", "***", new {area = "***"})',
true);
xmlHttpRequest.onerror = function() {
ShowAlert(ErrorText(), 'dark', true);
};
xmlHttpRequest.onloadend = function() {
window.$('#layoutLegalBill').html(html);
var response = ParseJson(xmlHttpRequest.responseText);
window.location = response.RedirectUrl;
}
xmlHttpRequest.send(formData);
});
The problem is that the file is downloaded but does not open and gives an error.
(The file is 17kb in size)

Requests are timing-out when light payload is applied to .Net Core Web Api

I have an API which basically receives a request and pushes it to an SQS queue, nothing complicated.
[HttpPost]
public ActionResult Post([FromBody]object message, [FromHeader] string source)
{
if (message== null)
return new UnsupportedMediaTypeResult();
if (PublishMessageToSQS(JsonConvert.SerializeObject(message),source))
return StatusCode(201);
return StatusCode(500);
}
private bool PublishMessage(string message, string source)
{
try
{
RetryWhenException.Do(
() =>
{
SendMessageRequest request = new SendMessageRequest()
{
MessageBody = message,
MessageAttributes = new Dictionary<string, MessageAttributeValue>(),
QueueUrl = "my queue",
};
if (!string.IsNullOrEmpty(source))
request.MessageAttributes.Add("source", new MessageAttributeValue()
{
StringValue = source,
DataType = "String"
});
var result = sqsClient.SendMessageAsync(request).Result;
}, 3, 1000);
return true;
}
catch (Exception e)
{
//log
throw;
}
}
This API is containerized and deployed to AWS ECS on low resources machine (0.25 VCpu, 512 MB RAM).
When applying a light load on the API (10 requests per second), requests start to timeout after a while.
I stopped receiving timeouts when applying one of:
1- Use more resources (2 VCPU, 4GB RAM)
2- make my action async.
[HttpPost]
public async Task<ActionResult> Post([FromBody]object message, [FromHeader] string source)
{
if (message== null)
return new UnsupportedMediaTypeResult();
if (await PublishMessageToSQS(JsonConvert.SerializeObject(message), source))
return StatusCode(201);
return StatusCode(500);
}
private async Task<bool> PublishMessage(string message, string source)
{
try
{
await RetryWhenException.Do(
async () =>
{
SendMessageRequest request = new SendMessageRequest()
{
MessageBody = message,
MessageAttributes = new Dictionary<string, MessageAttributeValue>(),
QueueUrl = "my queue",
};
if (!string.IsNullOrEmpty(source))
request.MessageAttributes.Add("source", new MessageAttributeValue()
{
StringValue = source,
DataType = "String"
});
var result = await sqsClient.SendMessageAsync(request);
}, 3, 1000);
return true;
}
catch (Exception e)
{
//log
throw;
}
}
RetryWhenException code:
public static class RetryWhenException
{
public static void Do(Action action, int maxAttemptCount = 3, int retryInterval = 1000)
{
var exceptions = new List<Exception>();
for (var attempted = 0; attempted < maxAttemptCount; attempted++)
{
try
{
if (attempted > 0)
{
Thread.Sleep(retryInterval);
}
action();
return;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
I know that Async frees threads, but pushing to SQS is not that costy and the number of requests is not that high.
I really don't understand why I was getting timeouts when applying such low payload, and why async did the trick, any explanation?

asp.net core razor page calling rest service and how to wait for response.

I have a razor page that calls a rest service to find the geocodes for a supplied address. The call uses a event triggered callback when it completes the lookup. Everything is working, but the timing is off. By the time the callback finishes, the page is already drawn, and I need the results from the callback to properly draw the page.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Northwind.ModelsDB;
using System.Runtime.Serialization.Json;
using BingMapsRESTToolkit;
using System.Net;
namespace Northwind.Pages.CustomerPages
{
public class DetailsModel : PageModel
{
private readonly Northwind.ModelsDB.NorthwindContext _context;
private readonly IOptions<MyConfig> config;
public string BingMapKey { get; private set; }
public double latitude { get; private set; }
public double longitude { get; private set; }
public string query { get; private set; }
public VIndividualCustomer VIndividualCustomer { get; private set; }
public DetailsModel(Northwind.ModelsDB.NorthwindContext context, IOptions<MyConfig> configg)
{
_context = context;
this.config = configg;
BingMapKey = config.Value.BingMapKey;
}
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
VIndividualCustomer = await _context.VIndividualCustomer
.AsNoTracking()
.FirstOrDefaultAsync(m => m.BusinessEntityId == id);
if (VIndividualCustomer == null)
{
return NotFound();
}
query = VIndividualCustomer.AddressLine1 + " " +
VIndividualCustomer.AddressLine2 + ", " +
VIndividualCustomer.City + ", " +
VIndividualCustomer.StateProvinceName + ", " +
VIndividualCustomer.PostalCode;
query = "1 Microsoft Way, Redmond, WA";
Uri geocodeRequest = new Uri(string.Format("http://dev.virtualearth.net/REST/v1/Locations?q={0}&key={1}", query, BingMapKey));
GetResponse(geocodeRequest, (x) =>
{
var location = (BingMapsRESTToolkit.Location)x.ResourceSets[0].Resources[0];
latitude = location.GeocodePoints[0].Coordinates[0];
longitude = location.GeocodePoints[0].Coordinates[1];
});
return Page();
}
private void GetResponse(Uri uri, Action<Response> callback)
{
System.Net.WebClient wc = new WebClient();
wc.OpenReadCompleted += (o, a) =>
{
if (callback != null)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Response));
callback(ser.ReadObject(a.Result) as Response);
}
};
wc.OpenReadAsync(uri);
}
}
}
Your method will never be updated because the response is already sent to the client. You need to block the method (use HttpClient instead) and wait for the response:
public async Task<IActionResult> OnGetAsync(int? id)
{
// this "reads" better
if (id.HasValue)
{
return NotFound();
}
VIndividualCustomer = await _context.VIndividualCustomer
.AsNoTracking()
.FirstOrDefaultAsync(m => m.BusinessEntityId == id);
if (VIndividualCustomer == null)
{
return NotFound();
}
query = VIndividualCustomer.AddressLine1 + " " +
VIndividualCustomer.AddressLine2 + ", " +
VIndividualCustomer.City + ", " +
VIndividualCustomer.StateProvinceName + ", " +
VIndividualCustomer.PostalCode;
query = "1 Microsoft Way, Redmond, WA";
// string interpolation
//https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
var url = $"http://dev.virtualearth.net/REST/v1/Locations?q={query}&key={BingMapKey}";
var geocodeRequest = new Uri(url);
var ser = new DataContractJsonSerializer(typeof(Response));
var response = await (new HttpClient()).GetAsync(geocodeRequest);
var json = await response.Content.ReadAsStringAsync();
var x = ser.ReadObject(json) as Response;
var location = (BingMapsRESTToolkit.Location)x.ResourceSets[0].Resources[0];
latitude = location.GeocodePoints[0].Coordinates[0];
longitude = location.GeocodePoints[0].Coordinates[1];
return Page();
}
The most closed code to your own code is this snippet:
private async Task GetResponseAsync(Uri uri, Action<Response> callback) {
System.Net.Http.HttpClient wc = new HttpClient();
var response = await wc.GetAsync(uri);
if (callback != null) {
var stream = await response.Content.ReadAsStreamAsync();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Response));
callback(ser.ReadObject(stream) as Response);
}
}
and call it just like:
await GetResponse(geocodeRequest, (x) =>
{
/ bla bla bla...
});
However, it's not a good refactored code. But can do the job.

asp.net core web api cant send object from client side

this is server code who cath the request from client side
[HttpPost("Add")]
public async Task<IActionResult> Add([FromBody]RequestAdd person)
{
if(person != null){
return Ok("good");
}
return Ok("false");
}
this is code is client post there i add to multipart json and image bytes
public Task<HttpResponseMessage> Uploads(Person person, List<FileInfo> files)
{
try
{
var jsonToSend = JsonConvert.SerializeObject(person, Formatting.None);
var multipart = new MultipartFormDataContent();
var body = new StringContent(jsonToSend, Encoding.UTF8, "application/json");
multipart.Add(body, "JsonDetails");
foreach (var item in files)
{
var fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes(item.FullName));
multipart.Add(fileContent, item.FullName);
}
var client = new HttpClient();
client.BaseAddress = new Uri(BASE_URL);
return client.PostAsync("Add", multipart);
}
catch
{
return null;
}
}
code where i use this method there i have error
static void Main(string[] args)
{
Method2();
Console.ReadLine();
}
static void Method2()
{
UploadMultiPart uploadMultiPart = new UploadMultiPart();
List<FileInfo> fileInfos = new List<FileInfo>()
{
new FileInfo(#"C:\asd\full-metal-jacket.png"),
new FileInfo(#"C:\asd\full-metal-jacket.png"),
new FileInfo(#"C:\asd\full-metal-jacket.png")
};
Person person = new Person
{
Name = "Adilbek",
SureName = "Ramazanov",
Position = "God",
Group = "heaven",
Phone = 123123
};
var result = loadMultiPart.Uploads(person,fileInfos).Result;
Console.WriteLine("Status is " + result.StatusCode);
}
error code is Status is UnsupportedMediaType
i dont have idea how to send to server, please help me sorry my bad english
Use the [FromForm] attribute, not [FromBody] attribute.
[HttpPost("Add")]
public async Task<IActionResult> Add([FromForm]RequestAdd person)
{
if(person != null){
return Ok("good");
}
return Ok("false");
}

Xamarin Forms http async request

i am trying to make an asynchronous call to a webservice.
I would like to make this call when opening the app (App.xaml.cs).
According to the answer that comes back to me, it has to navigate to a particular page
But I do not work.
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
try
{
CheckLogin().Wait();
}
catch (Exception e)
{
var t = e;
}
}
private static async Task CheckLogin()
{
try
{
var login = new Login
{
Email = "test#test.com",
Password = "test",
};
var client = new HttpClient { BaseAddress = new Uri("http://www.api.com/test/") };
var data = JsonConvert.SerializeObject(login);
var content = new StringContent(data, Encoding.UTF8, "application/json");
var response = await client.PostAsync(#"api/it-IT/auth/token", content); //crash without error, freeze
if (response.IsSuccessStatusCode)
{
var successResult = JsonConvert.DeserializeObject<HttpResponseMessage>(response.Content.ReadAsStringAsync().Result);
if (successResult != null)
{
//return true;
}
else
{
//return false;
}
}
}
catch (Exception e)
{
var t = e;
}
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<NavigationPage>();
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<MainPage2>();
Container.RegisterTypeForNavigation<MainPage3>();
}
}
When does the postasync call does not go more forward, not I get no errors, but does not proceed.
But if I try the same code in an application console, everything works fine, why?
class Program
{
static void Main(string[] args)
{
Console.WriteLine("A");
CheckLogin().Wait();
Console.WriteLine("K");
Console.ReadKey();
}
private static async Task CheckLogin()
{
try
{
var login = new Login
{
Email = "test#test.com",
Password = "#test",
};
var client = new HttpClient { BaseAddress = new Uri("http://www.api.com/test/") };
var data = JsonConvert.SerializeObject(login);
var content = new StringContent(data, Encoding.UTF8, "application/json");
var response = await client.PostAsync(#"api/it-IT/auth/token", content);
if (response.IsSuccessStatusCode)
{
}
}
catch (Exception e)
{
var t = e;
}
}
}
If I try to do the same operation within a command with wait I do not work the same error, but if I do await, it will work fine, but in App.xaml.cs in OnInitialized() I can not put await
public DelegateCommand callCommand { get; set; }
public MainPage2ViewModel()
{
callCommand = new DelegateCommand(Call);
}
private void Call()
{
//await CheckLogin(); // work
CheckLogin().Wait(); // not work the same problem
var i = "pippo";
}
private async Task CheckLogin()
{
....
}
Is there anything to set with xamarin or with prism?
I've also the same strange error...
i fix with this workaround (use an async void that wrap async task)...
public App()
{
InitializeComponent();
Current.MainPage = new LoadingPage();
}
protected override void OnStart()
{
MagicInit();
base.OnStart();
}
public static async void MagicInit()
{
var f = await FileSystem.Current.LocalStorage.CreateFileAsync("db.sqlite", CreationCollisionOption.OpenIfExists);
DbConnection = f.Path;
await DataService.DbFill();
User = await DataService.Instance.Table<SpUser>().FirstOrDefaultAsync();
Current.MainPage = User != null ? (Page)new MainPage() : new LoginPage();
}

Categories

Resources