I wrote an HTTP server with Dart, and now I want to parse form submits. Specifically, I want to handle x-url-form-encoded form submit from HTML forms. How can I do this with the dart:io library?
Use the HttpBodyHandler class to read in the body of an HTTP request and turn it into something useful. In the case of a form submit, you can convert it to a Map.
import 'dart:io';
main() {
HttpServer.bind('0.0.0.0', 8888).then((HttpServer server) {
server.listen((HttpRequest req) {
if (req.uri.path == '/submit' && req.method == 'POST') {
print('received submit');
HttpBodyHandler.processRequest(req).then((HttpBody body) {
print(body.body.runtimeType); // Map
req.response.headers.add('Access-Control-Allow-Origin', '*');
req.response.headers.add('Content-Type', 'text/plain');
req.response.statusCode = 201;
req.response.write(body.body.toString());
req.response.close();
})
.catchError((e) => print('Error parsing body: $e'));
}
});
});
}
Related
I call the constructor of OAuth2Strategy in app.controller.ts, i send it the 2 params it needs: options with clientID, callbackURL etc, and a verify callback function. But i have this error, looks like i don't send a verify callback function to the constructor, but i did. The error happens in node_modules/passport-oauth2/lib/strategy.js.
app.controller.ts:
import { Body, Controller, Get, Post, Query, Res, Req, Next, UnauthorizedException, UseGuards } from '#nestjs/common';
import { PartieService, JoueurService, CanalService } from './app.service';
import { Joueur, Partie, Canal } from './app.entity';
import { AuthGuard } from '#nestjs/passport';
import passport = require("passport");
import OAuth2Strategy = require("passport-oauth2");
//var OAuth2Strategy = require('passport-oauth2').OAuth2Strategy;
//import OAuth2Strategy from 'passport-oauth2';
#Controller()
export class AppController {
constructor(private readonly partieService: PartieService, private readonly joueurService: JoueurService,
private readonly canalService: CanalService) {}
#Get('/oauth2')
async oauth2(#Req() req, #Res() res, #Next() next) {
passport.use('oauth2', new OAuth2Strategy(
{
clientID: 'my_clientID',
clientSecret: 'my_clientSecret',
authorizationURL: 'https://api.abc.com/oauth/authorize',
tokenURL: 'https://api.abc.fr/oauth/token',
callbackURL: 'http://localhost:3000/Connection'
},
async (accessToken: string, refreshToken: string, profile: any, done: Function) => {
//console.log(profile);
try {
if (!accessToken || !refreshToken || !profile) {
return done(new UnauthorizedException(), false);
}
const user: Joueur = await this.joueurService.findOrCreate(profile);
return done(null, user);
} catch (err) {
return done(new UnauthorizedException(), false);
}
} ));
passport.authenticate('oauth2');
return res.sendFile("/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/public/connection.html");
}
#Get('/Connection')
//#UseGuards(AuthGuard('oauth2'))
async Callback(#Req() req, #Res() res, #Next() next) {
passport.authenticate('oauth2', (err, user, info) => {
if (err || !user) {
req.session.destroy();
return res.status(401).json({
message: 'Unauthorized',
});
}
req.user = user;
return next();
})(req, res, next);
const utilisateur: Joueur = req.user;
console.log(utilisateur);
}
[...]
}
node_modules/passport-oauth2/lib/strategy.js:
function OAuth2Strategy(options, verify) {
if (typeof options == 'function') {
verify = options;
options = undefined;
}
options = options || {};
if (!verify) { throw new TypeError('OAuth2Strategy requires a verify callback'); }
[...]
}
Error:
[Nest] 6550 - 05/02/2023, 18:34:54 ERROR [ExceptionHandler] OAuth2Strategy requires a verify callback
TypeError: OAuth2Strategy requires a verify callback
at new OAuth2Strategy (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/passport-oauth2/lib/strategy.js:84:24)
at Injector.instantiateClass (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:340:19)
at callback (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:53:45)
at Injector.resolveConstructorParams (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:132:24)
at Injector.loadInstance (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:57:13)
at Injector.loadProvider (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/injector.js:84:9)
at async Promise.all (index 3)
at InstanceLoader.createInstancesOfProviders (/home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/instance-loader.js:47:9)
at /home/user42/Documents/Projets/ft_transcendence/services/pong/pong/node_modules/#nestjs/core/injector/instance-loader.js:32:13
at async Promise.all (index 1)
`
I tried to change the import OAuth2Strategy many times (like the commented lines). I tried a lot of things but i cannot remember all. I found no answer in internet, and no more with the not-so-well-chatgpt.
I am new in nestjs and this error is weird for me, how can it ask me a parameter that i send ? Can someone help me to resolve this pls ? :)
Sorry if the answer is obvious :/ , it's my first API with nestjs
The error was coming from another file, my bad, it's fixed.
I cannot figure out why Axios is changing my request's content-type on retry.
I am creating an axios instance as follows (notice global default header):
import axios, { type AxiosInstance } from "axios";
const api: AxiosInstance = axios.create({
baseURL: "https://localhost:44316/",
});
export default api;
I import this instance in various components within my vue3 app. When my token has expired and I detect a 401, I use the interceptor to refresh my token and retry the call as follows (using a wait pattern to queue multiple requests and prevent requesting multiple refresh tokens):
axios.interceptors.request.use(
(config) => {
const authStore = useAuthStore();
if (!authStore.loggedIn) {
authStore.setUserFromStorage();
if (!authStore.loggedIn) {
return config;
}
}
if (config?.headers && authStore.user.accessToken) {
config.headers = {
Authorization: `Bearer ${authStore.user.accessToken}`,
};
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(res) => {
return res;
},
async (err) => {
if (err.response.status === 401 && !err.config._retry) {
console.log("new token required");
err.config._retry = true;
const authStore = useAuthStore();
if (!authStore.isRefreshing) {
authStore.isRefreshing = true;
return new Promise((resolve, reject) => {
console.log("refreshing token");
axios
.post("auth/refreshToken", {
token: authStore.user?.refreshToken,
})
.then((res) => {
authStore.setUserInfo(res.data as User);
console.log("refresh token received", err.config, res.data);
resolve(axios(err.config));
})
.catch(() => {
console.log("refresh token ERROR");
authStore.logout();
})
.finally(() => {
authStore.isRefreshing = false;
});
});
} else {
// not the first request, wait for first request to finish
return new Promise((resolve, reject) => {
const intervalId = setInterval(() => {
console.log("refresh token - waiting");
if (!authStore.isRefreshing) {
clearInterval(intervalId);
console.log("refresh token - waiting resolved", err.config);
resolve(axios(err.config));
}
}, 100);
});
}
}
return Promise.reject(err);
}
);
But when axios retries the post request, it changes the content-type:
versus the original request (with content-type application/json)
I've read every post/example I could possible find with no luck, I am relatively new to axios and any guidance/examples/documentation is greatly appreciated, I'm against the wall.
To clarify, I used this pattern because it was the most complete example I was able to put together using many different sources, I would appreciate if someone had a better pattern.
Here's your problem...
config.headers = {
Authorization: `Bearer ${authStore.user.accessToken}`,
};
You're completely overwriting the headers object in your request interceptor, leaving it bereft of everything other than Authorization.
Because the replayed err.config has already serialised the request body into a string, removing the previously calculated content-type header means the client has to infer a plain string type.
What you should do instead is directly set the new header value without overwriting the entire object.
config.headers.Authorization = `Bearer ${authStore.user.accessToken}`;
See this answer for an approach to queuing requests behind an in-progress (re)authentication request that doesn't involve intervals or timeouts.
I've been successfully getting the list of mails in inbox using microsoft graph rest api but i'm having tough time to understand documentation on how to download attachments from mail.
For example : This question stackoverflow answer speaks about what i intend to achieve but i don't understand what is message_id in the endpoint mentioned : https://outlook.office.com/api/v2.0/me/messages/{message_id}/attachments
UPDATE
i was able to get the details of attachment using following endpoint : https://graph.microsoft.com/v1.0/me/messages/{id}/attachments and got the following response.
I was under an impression that response would probably contain link to download the attachment, however the response contains key called contentBytes which i guess is the encrypted content of file.
For attachment resource of file type contentBytes property returns
base64-encoded contents of the file
Example
The following Node.js example demonstrates how to get attachment properties along with attachment content (there is a dependency to request library):
const attachment = await getAttachment(
userId,
mesasageId,
attachmentId,
accessToken
);
const fileContent = new Buffer(attachment.contentBytes, 'base64');
//...
where
const requestAsync = options => {
return new Promise((resolve, reject) => {
request(options, (error, res, body) => {
if (!error && res.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
};
const getAttachment = (userId, messageId, attachmentId, accessToken) => {
return requestAsync({
url: `https://graph.microsoft.com/v1.0/users/${userId}/messages/${messageId}/attachments/${attachmentId}`,
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json;odata.metadata=none"
}
}).then(data => {
return JSON.parse(data);
});
};
Update
The following example demonstrates how to download attachment as a file in a browser
try {
const attachment = await getAttachment(
userId,
mesasageId,
attachmentId,
accessToken
);
download("data:application/pdf;base64," + attachment.contentBytes, "Sample.pdf","application/pdf");
} catch (ex) {
console.log(ex);
}
where
async function getAttachment(userId, messageId, attachmentId, accessToken){
const res = await fetch(
`https://graph.microsoft.com/v1.0/users/${userId}/messages/${messageId}/attachments/${attachmentId}`,
{
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json;odata.metadata=none"
}
}
);
return res.json();
}
Dependency: download.js library
I don't know if this would help but you just have to add /$value at the end of your request :
https://graph.microsoft.com/v1.0/me/messages/{message_id}/attachments/{attachment_id}/$value
I have a Dart application running on the server side. It is listening at a specific port and working fine. The problem is: my listener is responding to the GET of the favorite icon (favicon).
How can I avoid that?
EDIT: give some code example.
import 'dart:io';
void main() {
print("Starting server.");
HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4041)
.then(listenForRequests)
.catchError((e) => print (e.toString()));
}
listenForRequests(HttpServer _server) {
_server.listen((HttpRequest request) {
if (request.method == 'GET') {
handleGet(request);
} else {
request.response.statusCode = HttpStatus.METHOD_NOT_ALLOWED;
request.response.write("Unsupported request: ${request.method}.");
request.response.close();
}
},
onDone: () => print('No more requests.'),
onError: (e) => print(e.toString()) );
}
void handleGet(HttpRequest request) {
int requestNumber = 1;
print(requestNumber); // This shows me the request number. Just for information.
print(request.uri); // This shows me the request from the client browser.
request.response.statusCode = HttpStatus.OK;
request.response.close();
}
This is the output of this code:
1
/SOME_REQUEST_FROM_THE_BROWSER
2
/favicon.ico
You can check the requested resource and generate proper response for requests to 'favicon.ico' like
void handleGet(HttpRequest request) {
int requestNumber = 1;
print(requestNumber++); // This shows me the request number.
print(request.uri); // This shows me the request from the client browser.
if(request.requestedUri.path != '/favicon.ico') {
request.response.statusCode = HttpStatus.NOT_FOUND;
} else {
request.response.statusCode = HttpStatus.OK;
}
request.response.close();
}
I'm trying to make a contact form in Dart. So far I can understand the server needs to include something like the following:
server
app.addRequestHandler(
(req) => req.method == 'POST' && req.path == '/adduser',
(req, res) {
//process json data
};
}
);
And a form for the client:
<form method="post" action="/adduser">
<fieldset>
<legend>Add a user</legend>
<p><label>First name</label>
<input name="user[first_name]"/></p>
<p><label>Email</label>
<input name="user[email]"/></p>
<p class="actions"><input type="submit" value="Save"/></p>
</fieldset>
</form>
What needs to be done on the client side to get the json data to the server side?
Is json necessary to process the form or is there a better way?
You can submit your form to server directly. The content will be sent URL-encoded in the body of the post request. On server, you can decode datas with the query_string package available on pub.
Add query_string to your pubspec.yaml file:
dependencies:
query_string: ">=1.0.0 <2.0.0"
Your server code can looks like :
import 'dart:io';
import 'package:query_string/query_string.dart';
main() {
final server = new HttpServer();
server.listen('127.0.0.1', 8081);
server.addRequestHandler((req) => req.method.toUpperCase() == 'POST'
&& req.path == '/adduser', (request, response) {
readStreamAsString(request.inputStream).then((body) {
final params = QueryString.parse("?${body}");
print(params["user[first_name]"]);
print(params["user[email]"]);
response.statusCode = HttpStatus.CREATED;
response.contentLength = 0;
response.outputStream.close();
});
});
}
Future<String> readStreamAsString(InputStream stream) {
final completer = new Completer();
final sb = new StringBuffer();
final sis = new StringInputStream(stream);
sis
..onData = () { sb.add(sis.read()); }
..onClosed = () { completer.complete(sb.toString()); }
..onError = (e) { completer.completeException(e); };
return completer.future;
}