Flutter web app does not work with local golang http server - flutter

I know it sounds like a beginner's question, but here I go:
I've developed a small app to be used locally in restaurants for the waiters to be able to take orders on their phones. Pretty basic. I also developed a simple go HTTP server to implement the necessary API. It works fine on the "mobile" version, both in the emulator and actual devices.
The next step is to port the app so it can be used by the customers in their phones' browsers.
When I changed it to the web version, it worked correctly in Chrome and Edge, at least in terms of UI and navigation. But I am not able to use the API because I get the XMLHTTPError in all requests (that's what the error capture returns, I don't know how to get more details on that).
I did a little research on the matter, and one of the answers includes changing configurations in Chrome, etc., which would be impossible for the customers to do. Also, it seems that a possibility is changing the go server from HTTP to HTTPS.
This is my go lang HTTP server code (part of)
func main() {
r := mux.NewRouter()
http.Handle("/", r)
r.HandleFunc("/api/getgarcon", get_garcon).Methods("GET")
log.Fatal(http.ListenAndServe(":8000", r))
}
get_garcon is a function that returns a JSON list of the registered waiters for login. As I said before, it works correctly if the client is a mobile app.
This is the flutter/dart code in the app
Future<List<Garcon>> getGarcon() async {
var data;
try {
print(glbHttpServer + "api/getgarcon");
data = await http.get(
glbHttpServer + "api/getgarcon",
headers: {'Content-Type': 'application/json','Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Methods':'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS',
'Access-Control-Allow-Headers':'X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method,Access-Control-Request-Headers, Authorization'},
);
} catch (e) {
print("getgarcon=" + e.toString());
}
etc...
glbHttpServer is a variable whose value is like "http://192.168.29.94:8000/"
I added those headers trying to get this to work for the web app. Previously (for the mobile app) there was only the 'Content-Type': 'application/json' header.
As additional information, both the web app and the go server are on the same machine. If I type http://192.168.29.94:8000/api/getgarcon in a Chrome window (or Edge window), it works. Same with localhost or 127.0.0.1.
So what I'm asking here is: which is the least complicated way to go? Adding some different code in my app? Changing the server from HTTP to HTTPS? Or is another solution available that I haven't found?
And most important: is it worth the effort, given the current state of the flutter web?
Thanks for any help.

You need to add those CORS headers to the response on the server, not the request from Flutter. In Go, it would look like this. You can create a middleware to handle this for all your endpoints.
func main() {
http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", ...)
w.Header().Set("Access-Control-Allow-Methods", ...)
w.Header().Set("Access-Control-Allow-Headers", ...)
...
})
...
}
Mobile apps do not have the same CORS restrictions, which is why you would not have noticed it. However, if you tried to load your Flutter website in a mobile browser, you would notice the same behavior.

Related

How do I save session cookie from response in flutter?

Let's say I log into an api with my flutter app. In an website, they automatically store the login cookie and then can use it. But in flutter app, how do I store the cookies and session? And how do I pass that into post to let the api know I have a valid login session?
Q: It's making me more confused, I just want the part to extract
cookie and how to use it.
There are a number of complexities, depending on exactly what you ultimately want to do.
But let's assume:
Your Flutter app makes an HTTP request (GET, PUT, etc.)
The server (e.g. your Flask app) returns cookies in the HTTP response (in the HTTP response header).
Let's further assume your HTTP code looks something like this:
Future<http.Response> fetchAlbum() {
return http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
}
In that case, you should be able to reference the cookies property of the Response object returned from the server.
SUGGESTION: See also these links:
Flutter For Web Cookie/Token Sessions and Authentication
Add Session Support to Flutter with Flutter Session

Why is the Fetch API not working on my machine? [duplicate]

My Chrome Extension performs a get request which works fine. Because testing is faster with snippets, I want to do the exact same thing in the Chrome Console or in the Chrome Snippets. Minimal example:
fetch(url, {
method: "GET"
}).then(response => response.text())
.then(html => console.log(html))
.catch(error => console.log(error))
Unfortunately, there I only get
TypeError: Failed to fetch for the error and
Failed to load resource: net::ERR_FAILED in Chrome's inline error marker
In my Chrome Extension I ran into a CORS issue so what I did in my AWS Lambda function was to set the headers to
const headers = {
'Content-Type': 'application/json',
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Credentials" : true
};
so I suppose CORS isn't the problem here. But I can't seem to figure out as to what differences it could make to have the requests run in the Extension vs. in the console/snippets. What could I be missing here?
I also do not see the request in AWS CloudWatch so I suppose it doesn't even leave my machine. I am testing on a Chrome User that has 0 extensions installed, same happens in incognito
To circle out any issues with my server I have also inserted the examples from https://lockevn.medium.com/snippet-to-fetch-remote-rest-api-and-display-in-console-of-chrome-devtool-6896a7cd6041
async function testGet() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts')
console.log(await response.json())
}
async function testPost() {
let r = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
lockevn: 1
}),
})
console.log(await r.json())
}
testGet()
testPost()
Chrome's Network tab shows the request as stalled
The linked 'explanation' gives
Queueing: The browser queues requests when:
There are higher priority requests.
There are already six TCP connections open for this origin, which is
the limit. Applies to HTTP/1.0 and HTTP/1.1 only.
The browser is briefly allocating space in the disk cache
Stalled: The request could be stalled for any of the reasons described in Queueing.
Higher priority seems odd, 6 connections can't be the issue either since I have restarted my browser before testing, and the disk cache issue doesn't sound like the problem either. I'm not macOS with no anti virus
I managed to find the issue. In order to avoid potentially privileging my requests by opening the Chrome Developer Console in my AWS dashboard tab, I have created a new tab (chrome://new-tab-page/) and performed the requests in the console. This returned the errors described.
When I have updated my question with the example code I wanted to confirm if it was running before asking someone to try it if it works on their machine. For quick runtime-validation I opened the Console in the Stackoverflow tab and it worked. I only wanted to check if the code can be interpreted but it turned out to actually return a result. The same is valid for my AWS instance, if I run it on a https website it works fine. No idea why this is not documented but "disk cache" is mentioned as a potential error.
tldr don't open Chrome Console in new tab for requests in the console, use any website. This may have to do with CORS headers only working if the request doesn't have empty headers to begin with maybe (?)
I specifically avoided using a website console instance for testing because I wanted to prevent potential cookies on the AWS page from doing something that someone else couldn't do on their machine. Good thinking bad result haha
Thank you so much for your comments suggesting the help, much appreciated.

Code for a PWA to respond to a 200

I want to test my Progressive web application for offline status. I want that if my webapp if offline, it should respond with a 200 status code. What can be the possible code for that?
Your question is not detailed enough to give a detailed answer but in general:
it sounds like the 200 status code is from an HTTP request, in which case if your PWA uses javascript (e.g. via ajax) to make the remote API calls (e.g. GET) then you just handle the state in the returned event object, e.g. you have to handle other states (e.g. other types of failures too). For example see: https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX/Getting_Started
You also could also test the online/offline state before making the HTTP request.
See discussion of using navigator.onLine or adding
window.addEventListener('offline', function(e) {//do something}, false);
window.addEventListener('online', function(e) {//do something}, false);
Another example is I use Polymer webcomponents and firebase authentication for my PWAs and in Polymerfire it's simply a case of handling the online property.

How to send POST and GET requests from Sails app to outside Sails API

I am beginning to use SailsJS and i found it wonderful and powerful.
Can anybody please explain me how to send POST and GET requests to an API outside Sails and where do i actually write this request?
Thanks a lot and Happy 2016 everyone!!!
Edit:
Hello #arcseldon, thank you for been trying to help me.
I'll try to explain myself better, and show you my code.
I have an API, written in php (which i think is not relevant) which accepts POST, GET, PUT, DELETE methods. I use Postman for testings and everything looks OK.
I am trying to make an app in Sails, which GETs and POSTs requests to my API, but i dont know where is the best place to put the GET's and POST's codes.
In the model i already have the following to ask for a token to perform the other requests, and it works:
gettoken: function (requestnewtoken,tokenresult) {
if(!requestnewtoken) {
tokenresult(global.tokeng);
} else {
request({
headers: {
'User-agent' : 'develop',
'Content-Type' : 'application/x-www-form-urlencoded;charset=UTF-8',
'Content-Length' : '29',
'Authorization' : 'Basic ' + global.idsecret
},
uri: "https://myapi/oauth2/token",
method: "POST",
form: {
grant_type: "client_credentials"
}
}, function(error, response, body) {
var tokenjson = JSON.parse(body);
var token = tokenjson['access_token'];
global.tokeng = token;
tokenresult(token);
});
}
}
Then, i perform a GET request to another endpoint, which works:
listpublicroutes: function(requestnewtoken,cb) {
Model.gettoken(requestnewtoken,function(token) {
request({
headers: {
'Authorization' : 'Bearer ' + token
},
uri: "https://myapi/folder/file.json",
method: "GET",
timeout: 10000,
followRedirect: true,
maxRedirects: 10
}, function(error,response, body) {
if(error || (response.statusCode != 200)) {
Model.listpublicroutes(true,cb);
} else {
cb(null,JSON.parse(body));
}
});
});
}
My doubts are if this is the best way to write a POST and GET request or they could be more simple, and if the requests should be done in the controller (or anywhere else) instead of the model.
Can you give me an example of a POST and GET request?
Thanks a lot to everyone who's trying to understand me.
Your question isn't clear exactly what you are asking... Here are a few suggestions depending on what you wish to do.
If you are trying to call out and make an http request from within
server-side Sails code then I would recommend you take a look at the
NPM module request.
If you are talking about making get / post requests to test your API,
then use a web browser plugin / tool such as postman (also a
Chrome plugin of same name).
If you are talking about calling a completely different domain URL
using AJAX from within your web application client (via web browser)
then you can just use any AJAX approach (jquery / angular / whatever
client library you are using to make ajax calls) but be aware that the
domain you are calling to would have to have been setup with a cross
origin resource sharing (CORS).
You have control over your own CORS settings (allowing apps originating from other domains to call into your sails API from the browser, by updating config/cors.js settings.
Does this answer your question? If you need further help leave me a message below, and I'll try to assist.
Update Based On Question Update:
#Michi - ok, you wish to call your PHP api from within SailsJS - the three contenders in terms of location to do this are inside a Controller, a custom Model method, or within a custom service.
My "recommendation" is that most of the time, this logic sits within a Controller if it is logic that doesn't really need to be shared elsewhere. You could conceivably call into a Model.method(), but "usually" you want to think of the models as your domain data (certainly not always, if the responsibility for what you are doing truly belongs to a single Model etc which owns all the state interaction then the model might be the right place to locate that responsibility).
However, in your case, you are getting a token, and without seeing your model code (you have just provided the getToken function which doesn't really look like it is tied to a particular model..?) then I would say opt to invoke it within your controller if not needed elsewhere. Otherwise, refactor it out into a service. To quote the SailsJS documentation:
Services can be thought of as libraries which contain functions that
you might want to use in many places of your application. For example,
you might have an EmailService which wraps some default email message
boilerplate code that you would want to use in many parts of your
application. The main benefit of using services in Sails is that they
are globalized--you don't have to use require() to access them.
I have frequently put custom logic in Controllers, Models and Services, but for networking related logic etc, my preference is:
Controller if one-off logic.
Service if reusability required, or encapsulating the code as service improves readability / maintenance of the app
Use a model, only if you strongly believe the logic and responsibility is truly tied to that model - err on the side of caution here and use sparingly. But I acknowledge my recommendations may be construed as subjective - am basing them on what I believe to be good OOP practices in general terms.

Logging a user out when using HTTP Basic authentication

I want users to be able to log in via HTTP Basic authentication modes.
The problem is that I also want them to be able to log out again - weirdly browsers just don't seem to support that.
This is considered to be a social-hacking risk - user leaves their machine unlocked and their browser open and someone else can easily visit the site as them. Note that just closing the browser-tab is not enough to reset the token, so it could be an easy thing for users to miss.
So I've come up with a workaround, but it's a total cludge:
1) Redirect them to a Logoff page
2) On that page fire a script to ajax load another page with dummy credentials:
$j.ajax({
url: '<%:Url.Action("LogOff401", new { id = random })%>',
type: 'POST',
username: '<%:random%>',
password: '<%:random%>',
success: function () { alert('logged off'); }
});
3) That should always return 401 the first time (to force the new credentials to be passed) and then only accept the dummy credentials:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOff401(string id)
{
// if we've been passed HTTP authorisation
string httpAuth = this.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(httpAuth) &&
httpAuth.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
// build the string we expect - don't allow regular users to pass
byte[] enc = Encoding.UTF8.GetBytes(id + ':' + id);
string expected = "basic " + Convert.ToBase64String(enc);
if (string.Equals(httpAuth, expected, StringComparison.OrdinalIgnoreCase))
{
return Content("You are logged out.");
}
}
// return a request for an HTTP basic auth token, this will cause XmlHttp to pass the new header
this.Response.StatusCode = 401;
this.Response.StatusDescription = "Unauthorized";
this.Response.AppendHeader("WWW-Authenticate", "basic realm=\"My Realm\"");
return Content("Force AJAX component to sent header");
}
4) Now the random string credentials have been accepted and cached by the browser instead. When they visit another page it will try to use them, fail, and then prompt for the right ones.
Note that my code examples are using jQuery and ASP.Net MVC, but the same thing should be possible with any technology stack.
There's another way to do this in IE6 and above:
document.execCommand("ClearAuthenticationCache");
However that clears all authentication - they log out of my site and they're logged out of their e-mail too. So that's out.
Is there any better way to do this?
I've seen other questions on this, but they're 2 years old - is there any better way now in IE9, FX4, Chrome etc?
If there is no better way to do this can this cludge be relied upon? Is there any way to make it more robust?
The short anser is:
There is no reliable procedure for achieving a "logoff" using
HTTP Basic or Digest authentication given current implemenations of basic auth.
Such authentication works by having the client add an Authorization header
to the request.
If for a certain resource the server is not satisfied with the credentials provided (e.g. if there are none), it will responde with a
"401 Unauthorized" status code and request authentication. For that purpose it will provide a WWW-Authenticate header with the response.
A client need not wait for a server requesting authentication.
It may simply provide an Authorization header based on some local
assumptions (e.g. cached information from the last successful attempt).
While your outlined approach on "clearing" out authentication info has a good chance of working with a wide range of clients (namely widespread browsers),
there is absolutely no guarantee that a nother client might be "smarter" and
simply discriminate proper authentication data for your "logout" page and any other pages of the target site.
You will recognize a similar "problem" with using client side certificate based authentication.
As long as there is no explicit support from clients you might fight on lost ground.
So, if "logoff" is a concern, move over to any session based authentication.
If you have access to the implementation of authentication on the server side you might be able implementing a functionality that will disregard authentication information presented with Authorization header (if still identical to what has been presented during current "session) on request of your application level code (or provide some "timout" after which any credentials will be re-requested), so that the client will ask the user for providing "new" credentials (performing a new login).