I've got a few devices that interact with an Express app on a local server. For the purposes of a prototype demo I'm trying to make this as simple as possible. Among many other things, my local server interacts with images, and image data in the /uploads folder thusly:
(Express/NodeJS code):
app.route('/uploads')
//Send File to Android
.get( function (req, res) {
var images = fs.readdirSync('./uploads');
console.log(images[images.length-1]);
var imgpath = path.resolve('./uploads/' + images[images.length-1]);
res.sendFile(imgpath);
//res.end('Image List Sent')
})
//Receive image chip data from Android
.post( function (req, res) {
Console.log("insideit");
Console.log(req.body);
res.end('got something?')
});
This server code is receiving requests from C# Android code. The GET command works perfectly, so I will omit that Xamarin/C# code. The POST command from the android app is thus (In C#/Xamarin):
var rxcui = "198840";
string _url = string.Format(#"http://10.1.10.194:3000/uploads", rxcui);
string datastr = "test";
try
{
(new WebClient()).UploadString(_url, datastr);
}
catch
{
Console.WriteLine("Post Upload Error");
}
}
The server sees the post request, but returns 500. It appears that it's not routing properly, because It won't go into my post handling code and print a simple test string. Any thoughts on why the POST command is not being handled appropriately?
Try to replace res.end with res.send(). Something like this
.post( function (req, res) {
Console.log("insideit");
Console.log(req.body);
res.send('got something?');
});
This should work.
Related
Background:
My project consists of a Vue 2 Front end encased in an electron shell with an asp.net core web API backend.
What I am trying to do is get a google authorization token and refresh token so that I can create a folder and files within that folder on a users google drive. I also want to display a list of the folders content in my front end.
I have tried using the new Google Identity Services code in my front end but when launching my app and clicking on the google button I the following error
Error 400: invalid_request
If you’re the app developer, make sure that these request details comply with Google policies:
redirect_uri: storagerelay://file/?id=auth12850
I think this is because electron is seen more of a desktop app which according to the google documentation needs to use a loopback address to open the system browser and authenticate from there.
So I tried the google authentication api on my backend which is C# I have the google web authorization broker setup and when I run my backend and call my endpoint I get the google sign in page and can get the authorization token and refresh token.
Question:
Is there a way to capture the URL of the page that comes up for authentication so that I can put it in a child window in electron.
or a way to pass the data to the front end so that I can show the list of files to my users.
Would I still need to use a loopback address even though I am getting the authentication page from the web broker?
If I do need the loopback functionality would I be better off using the data from the Google Desktop Application example rather than the google Web broker?
To learn more about electron you can go Electron website.
For a web app I think your going to have an issue
Users will be redirected to this path after they have authenticated with Google. The path will be appended with the authorization code for access, and must have a protocol. It can’t contain URL fragments, relative paths, or wildcards, and can’t be a public IP address.
Not just because of the format but because its going to need to be a domain you can register.
If you go with an installed app then the redirect uri is https://127.0.0.1
Im not sure how you are going to get this to route back properly.
I ended up using the nodejs google api to get this working this is my code that now returns an auth token and refresh token.
This code opens a child window when the authorize button is clicked and loads the google login/account select. Once authorized it shows the app permission window. When a user clicks allow the url is invoked in the loopback of the created server and the auth file is created in the specified directory.
part one successful.. now on to getting everything else working.
/* Google authentication */
function createGoogleWindow() {
const http = require('http');
const path = require('path');
const service = google.drive('v3');
const TOKEN_DIR = path.join(process.env.APPDATA, 'home-inventory', 'bin');
const TOKEN_PATH = path.join(TOKEN_DIR, 'home-inventory.json');
const querystring = require('querystring');
let googleWindow = new BrowserWindow({
parent: win,
height: 600,
width: 400,
webPreferences: {
webSecurity: false,
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false
}
});
if (isDevelopment) googleWindow.webContents.openDevTools();
googleWindow.menuBarVisible = false;
googleWindow.on('closed', () => {
googleWindow = null;
});
const oauth2Client = new google.auth.OAuth2(
CLIENT_ID,
CLIENT_SECRET,
REDIRECT_URI
);
// check if we previously stored a token
fs.readFile(TOKEN_PATH, function (err, token){
if (err) {
getNewToken(oauth2Client);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
const url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
function callback (auth) {
service.files.list({
auth: auth,
q: `name contains '.bak'`,
pageSize: 50,
fields: "nextPageToken, files(id,name,size,parents,createdTime)",
}, function(err, response) {
if (err) {
console.error('The API returned an error: ', err);
return;
}
const files = response.files;
if (files.length === 0) {
console.warn('no files found');
} else {
console.warn('files', files, auth.credentials.access_token);
}
});
}
function getNewToken(oauth2Client, callback) {
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code !== 'EEXIST') {
throw err
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) {
throw err;
}
// console.debug('file was saved successfully');
googleWindow.close();
});
// console.warn('Token stored to: ', TOKEN_PATH);
}
function handler(request, response, server, callback) {
let qs = querystring.parse(require('url').parse(request.url).query);
oauth2Client.getToken(qs.code, function (err, tokens) {
if (err) {
console.error('Error getting oAuth tokens: ', err);
}
oauth2Client.credentials = tokens;
storeToken(tokens);
callback(oauth2Client)
server.close();
});
}
const server = http.createServer(function (request, response) {
handler(request, response, server, callback);
}).listen(8181, function() {
googleWindow.loadURL(url);
})
}
}
This question already has answers here:
How to delete a file after it was streamed in ASP.NET Core
(5 answers)
Closed 2 years ago.
I am working on an ASP.NET Core 2.1 API project, which will be consumed by an Angular App, and later on a mobile app. one of the required functionality is to zip and download a collection of files, the result zipped file is going to be large (1GB or more).
the code I have now is working as follows:
the Angular app requests the API and expects a blob response.
the API on the server creates a Zip file and reads it using memory stream.
the API returns the memory stream using File response.
the method that subscribes to the download service in Angular saves
the file.
what is happening now is when I click in the browser on the download button I have to wait for the download to be finished then the browser shows the default popup that allows the user to save and select where to save.
I was wondering if what I'm doing is correct and won't cause any memory problems in the future?
is there a better methodology where the file could be streamed smoothly, so when the download starts the browser directly shows the save message and shows the default browser progress bar?
angular code:
component click function:
download(event,id){
event.stopPropagation();
event.preventDefault();
this.myservice.Downloadservice(avatar_id).subscribe((res: any) => {
saveAs(res.data, res.filename);
});
}
service code:
DownloadAllservice(id): Observable<any> {
let authToken = localStorage.getItem('auth_token');
let _options = { headers: new Headers({ 'Authorization': `Bearer ${authToken}`, } ),responseType: ResponseContentType.Blob };
let formData = new FormData();
let options ={
type: "zip",
id: id
};
formData.append('options',JSON.stringify(options));
return this.http.post(this.baseUrl + "/api/Download/", formData, _options)
.map(response =>{
return {
'filename': this.getFileNameFromHttpResponse(response),
'data': response.blob()
} })
.catch(this.handleError);
}
.net core code:
[Authorize(Policy = "Admin")]
[DisableRequestSizeLimit]
[HttpPost("Download", Name = "Download")]
public async Task<IActionResult> Download()
{
// get files list then start creating the temp folder and zipped folder
var archive = Path.Combine(Directory.GetDirectoryRoot("wwwroot"), #"home\" + FilePath + #"\temp\" + "filename");
var temp = Path.Combine(Directory.GetDirectoryRoot("wwwroot"), #"home\" + FilePath + #"\temp");
if (!System.IO.Directory.Exists(temp))
{
Directory.CreateDirectory(temp);
}
Directory.CreateDirectory(archive);
try
{
foreach (var file_id in filelist)
{
var path = file.path;
if (!System.IO.File.Exists(Path.Combine(archive, Path.GetFileName(path)))) {
System.IO.File.Copy(path, Path.Combine(archive, Path.GetFileName(path)));
}
}
var archivezip = Path.Combine(Directory.GetDirectoryRoot("wwwroot"), #"home\" + FilePath + #"\temp\" + "filename" + ".zip");
// create a new archive
ZipFile.CreateFromDirectory(archive, archivezip);
var memory = new MemoryStream();
using (var stream = new FileStream(archivezip, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
Directory.EnumerateFiles(archive).ToList().ForEach(f => System.IO.File.Delete(f));
Directory.EnumerateDirectories(archive).ToList().ForEach(f => System.IO.Directory.Delete(f, true));
Directory.Delete(archive);
System.IO.File.Delete(archivezip);
return File(memory, "application/octet-stream","filename.zip");
}
catch (Exception ex)
{
return new BadRequestObjectResult(ex.Message);
}
}
please note that in the future not only angular app will use the API, but mobile apps will also be added
I do a similar thing (I guess everyone does because that's apparently how it's done with Angular). I just do a basic unlimited loading spinner because, so far, I haven't needed to worry about tracking the progress.
However, there are various guides to handling this out there for you to follow.
It seems to boil down to changing your request from a simple, standard get/post request to one that listens to the events of the response.
From the linked article:
this.http.get(url, {
reportProgress: true,
observe: 'events',
responseType: 'blob'
})
Important parts there are to ensure it wants progress reports and to observe events. Once the request is observing events, then you need to handle observing them (obviously).
That, however, is a longer more involved part of the article. Go forth and good luck.
Edit: Ah, the issue is actually API side you're worried about. Fair enough, there are a variety of similar questions that might be of use then.
I have a WebSite integrated with SignalR. It functions well, and it has a button which sends popup notification to all clients who are online. It works well when I click on the button.
My API is in another project but in the same Solution. I want to send the above notification by calling from the API side. Basically, a mobile app will send a request to API and then API will send a notification to all online web clients.
Below code runs and not gives the notification nor any error.
Is this fundamentally correct? Appreciate your help
API code (at WebAPI project)
[HttpGet]
public IEnumerable<string> WatchMe(int record_id)
{
GMapChatHub sendmsg = new GMapChatHub();
sendmsg.sendHelpMessage(record_id.ToString());
return "Done";
}
C# code (at Web project)
namespace GMapChat
{
public class GMapChatHub : Hub
{
public void sendHelpMessage(string token)
{
var context = GlobalHost.ConnectionManager.GetHubContext<GMapChatHub>();
context.Clients.All.helpMessageReceived(token, "Test help message");
}
}
}
Home.aspx file (at Web project)
var chat = $.connection.gMapChatHub;
$(document).ready(function () {
chat.client.helpMessageReceived = function (token,msg) {
console.log("helpMessageReceived: " + msg);
$('#helpMessageBody').html(msg)
$('#helpModal').modal('toggle');
};
}
You can not call that hub directly. Firs you need to install the .net client for SignalR from nuget. Then you need to initialize it like this :
[HttpGet]
public IEnumerable<string> WatchMe(int record_id)
{
using (var hubConnection = new HubConnection("your local host address"))
{
IHubProxy proxy= hubConnection.CreateHubProxy("GMapChatHub");
await hubConnection.Start();
proxy.Invoke("sendHelpMessage",record_id.ToString()); // invoke server method
}
// return sth. IEnumerable<string>
}
And opening a new connection per request may not be good idea you may make it per session (if you use) or static or time fashioned.
I have created a example of project app device from meteor.
Inside my code I created side server have created a CRUD with C # web api 2.0.
While meteor side I created on method inside main.js on the SERVER folder, where I'm going to point to the web api:
addStudent:function(FirstName,LastName,Email,Address){
Toast.info(result);
this.unblock();
var result = Meteor.http.call("POST","",
{
data:{
"FirstName": FirstName,
"LastName" : LastName,
"Email": Email,
"Address":Address
}
}
);
//Toast.info(result);
return result;
},`
and inside main.js I called the method in the client:
Template.createStudent.events({
'click #btnCreate':function(events){
events.preventDefault();
var Firstname='pippo';
var Lastname='pluto';
var Email='tizio1#libero.it';
var Address='via polo nord';
setTimeout(function() {
Meteor.call("addStudent",Firstname,Lastname,Email,Address, function(error, result) {
if (error) {
Toast.info("Client:" + error);
alert("Oops!!! Something went wrong!");
}
else {
Toast.info("Client:" + result);
Session.set('tx', result);
}
});
});
}
});
When I go to throw ("meteor run Android-device") the project concerned, and connect the usb of my phone, he does not call the service, but if I run it on the browser ("localhost: 3000"), everything works fine.
P.S.: In addition I have also enabled the enableCors on side Api .NET
Could you help me understand why?
Thank you
how to send message(text,image,videos) to whatsapp from C# Asp.net Application.
Sending bulk messages programmatically through WhatsApp is definitively against their Terms of Service, and even the author of the (third-party) API posted by andrey.shedko doesn't maintain it or accept any responsibility for it. There's a function within the app to send as many messages as you want to the people you actually know - use that instead.
Within their Legal section:
(iii) you will not attempt to reverse engineer, alter or modify any part of the Service
and
C. You agree not to use or launch any automated system, including without limitation, "robots," "spiders," "offline readers," etc. or "load testers" such as wget, apache bench, mswebstress, httpload, blitz, Xcode Automator, Android Monkey, etc., that accesses the Service in a manner that sends more request messages to the WhatsApp servers in a given period of time than a human can reasonably produce in the same period by using a WhatsApp application
They do not provide a public API, so there's no wiggle room here.
i find perfect solution on This Link.
Following code (C#) I used for sending message-
//Send ( button_click )
string from = "9199********";
string to = txtTo.Text;//Sender Mobile
string msg = txtMessage.Text;
WhatsApp wa = new WhatsApp(from, "BnXk*******B0=", "NickName", true, true);
wa.OnConnectSuccess += () =>
{
MessageBox.Show("Connected to whatsapp...");
wa.OnLoginSuccess += (phoneNumber, data) =>
{
wa.SendMessage(to, msg);
MessageBox.Show("Message Sent...");
};
wa.OnLoginFailed += (data) =>
{
MessageBox.Show("Login Failed : {0}", data);
};
wa.Login();
};
wa.OnConnectFailed += (ex) =>
{
MessageBox.Show("Connection Failed...");
};
wa.Connect();