FLASK request.json not working with React payload - python

I have a flask route that queries my DB for locations and services based on a radius. Testing and working with POSTMAN.
#bp.route('/provider-locations/<int:radius>', methods=['GET'])
def get_locations_from_radius(radius):
user_latitude = request.json['lat']
user_longitude = request.json['lng']
my_obj = {}
my_obj_container = []
locations = db_session.query(ProviderLocation).filter(\
func.acos(func.sin(func.radians(user_latitude)) \
* func.sin(func.radians(ProviderLocation.latitude)) + \
func.cos(func.radians(user_latitude)) * \
func.cos(func.radians(ProviderLocation.latitude)) * \
func.cos(func.radians(ProviderLocation.longitude) - \
(func.radians(user_longitude)))) * 6371 <= radius)
for location in locations:
services = db_session.query(ProviderService).select_from(ProviderService).join(\
ServiceLocationLink).filter(ServiceLocationLink.location_id == location.id).all()
my_obj = provider_location_list_schema.dump(location)
my_obj['services'] = [provider_service_list_schema.dump(service) for service in services]
my_obj_container.append(my_obj)
return jsonify(my_obj_container)
My error comes in trying to pass this req the expected params from my client (React). I keep receiving a 400 response.
export async function getLocationServicesByRadius(radius, user_address) {
try {
const body = {
lat: user_address.lat,
lng: user_address.lng
}
return await awsApiRequest({
method: 'GET',
path: `/provider-locations/${radius}`,
params: {
body: body
},
});
} catch (err) {
console.log(err.message)
}
}
const fetchLocationServices = async () => {
const userAddress = {
lat: 43.829640,
lng: -79.470310
}
const { data, success, errorMessage } = await getLocationServicesByRadius(userDistance, userAddress)
if (success) {
console.log('RADIUS =>', data)
} else {
console.log(errorMessage)
}
}
I must be missing something somewhere but can not figure out why the req is returning 400.
the body from the client looks as so:
{
lat: 43.82964
lng: -79.47031
}

In flask request.json is used to access POST request body, for GET requests use request.args.

Related

How do i implement login with FastAPI /token endpoint in Flutter? FastAPI(JWT + OAuth) Frontend & Flutter Backend

Been trying for days to implement login with the /token endpoint from fastAPI in my flutter app I have checked many tutorials online but none seems to show how to do it correctly when using JWT and OAuth. I decided to go with Dio uisng fullstacks tutorial on youtube i was able to understand Dio and implement it in my app. But i get this error in flutter when i hit the login button:
error: {detail: [{loc: [body, username], msg: field required, type: value_error.missing},
And this error on fastAPI:
"POST /token HTTP/1.1" 422 Unprocessable Entity
My flutter login.dart looks like this:
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import '../../dio/dio_client.dart';
import '../../dio/dio_token_manager.dart';
import 'logout.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final _controllerUsername = TextEditingController();
final _controllerPassword = TextEditingController();
var _username = '', _password = '';
#override
void initState() {
super.initState();
() async {
DioClient.dio.interceptors.add(TokenManager.instance);
await checkLogin();
}();
}
Future<void> checkLogin() async {
try {
var response = await DioClient.dio.get('http://10.0.2.2:8000/token');
if (response.statusCode == 200) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => const LogoutScreen()));
}
} on DioError catch (e) {
debugPrint("Status code: ${e.response?.statusCode}");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
decoration: const InputDecoration(labelText: 'Username'),
validator: (val) => val!.isEmpty ? 'Username Required' : null,
onSaved: (val) => _username = val!,
keyboardType: TextInputType.text,
controller: _controllerUsername,
autocorrect: false,
),
TextFormField(
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (val) => val!.isEmpty ? 'Password Required' : null,
onSaved: (val) => _password = val!,
controller: _controllerPassword,
keyboardType: TextInputType.text,
autocorrect: false,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
final form = _formKey.currentState;
if (form!.validate()) {
form.save();
final snackbar = SnackBar(
duration: const Duration(seconds: 30),
content: Row(
children: const [
CircularProgressIndicator(),
Text(" Logging In...")
],
),
);
ScaffoldMessenger.of(context).showSnackBar(snackbar);
await Future.delayed(const Duration(seconds: 2));
try {
final body = {
"username": _username,
"password": _password,
};
var response = await DioClient.dio.post(
'http://10.0.2.2:8000/token',
//data: jsonEncode(body),
data: {
"username": _username,
"password": _password,
},
);
if (response.statusCode == 200) {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => const LogoutScreen()));
}
} on DioError catch (e) {
debugPrint("error: ${e.response?.data}");
}
ScaffoldMessenger.of(context).hideCurrentSnackBar();
}
},
child: const Text('Log In'),
)
],
),
),
),
);
}
}
From the fastAPI localhost:8000/docs, the /token endpoint returns the 200 OK response if you are testing the API on insomnia or postman but does not work when you try to implement the same logic on a frontend app like flutter. The fastAPI return the json with an access_token:
{
"access_token": "string",
"token_type": "string"
}
The code for the fastAPI endpoints comes from this default FastAPI Docs with username as johndoe
For the Dio_token_manager.dart :
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
class TokenManager extends Interceptor {
static final TokenManager _instance = TokenManager._internal();
static TokenManager get instance => _instance;
TokenManager._internal();
String? _token;
#override
void onResponse(
Response response,
ResponseInterceptorHandler handler,
) {
if (response.statusCode == 200) {
var data = Map<String, dynamic>.from(response.data);
if (data['set-token'] != null) {
saveToken(data['token']);
}
} else if (response.statusCode == 401) {
clearToken();
}
super.onResponse(response, handler);
}
#override
void onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) {
options.headers['Token'] = _token;
return super.onRequest(options, handler);
}
Future<void> initToken() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_token = prefs.getString('token');
}
void saveToken(String newToken) async {
debugPrint('new token $newToken');
if (_token != newToken) {
_token = newToken;
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('token', _token!);
}
}
So for the dio_client.dart
class DioClient {
static Dio dio = Dio();
static const baseUrl = "http://10.0.2.2:8000";
static const productsEndpoint = "$baseUrl/products";}
What i have tried so far:
I tried adding the jsonEncode to the data as can be seen from the commented code in the login code.
I tried adding headers to the dioClient.post but it still doesnt work.
The fastAPI login route:
#app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
The backend works just fine i think. Maybe there is something i am not doing right. Please help. Thank you.
OAuth2PasswordRequestForm expects its data as POST form variables, not as JSON.
You can change the format sent by referencing the example given in the dio documentation for how to "Send form data" instead:
var formData = FormData.fromMap({
'name': 'wendux',
'age': 25,
});
var response = await dio.post('/info', data: formData);
In your case this would reference the username/password variables instead:
final body = FormData.fromMap({
"username": _username,
"password": _password,
});
var response = await DioClient.dio.post(
'http://10.0.2.2:8000/token',
data: body,
);

react-admin fetchUtils doesn't get headers

I was trying to follow this tutorial https://marmelab.com/react-admin/Tutorial.html#connecting-to-a-real-api
my server is a simple python flask as endpoint server, as the tutorial said, i need set x-total-count into header, so i do it in my flask app like:
#posts.get("/posts")
def all():
print("headers-->", request.headers)
payload = [
{
"userId": 10,
"id": 100,
"title": "at nam consequatur ea labore ea harum",
"body": "cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut",
"views": 1999,
"published_at": "12/10/2022"
}
]
# X-Total-Count
headers = {"X-Total-Count": "20", "Access-Control-Expose-Headers": "x-total-count"}
response = jsonify(payload)
return response, HTTP_200_OK, headers
and after i do curl or refresh my browser, i can see the headers has been sent to client,
and my reactjs code like below:
import { fetchUtils } from "react-admin";
import { stringify} from "query-string";
const apiUrl = "http://192.168.31.40:5001/posts"
// const apiUrl ="https://jsonplaceholder.typicode.com";
// const httpClient = fetchUtils.fetchJson;
const authToken="123123";
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json', "Access-Control-Expose-Headers": "X-Total-Count"});
}
options.headers.set('Authorization', authToken);
// options.headers.set(Access-Control-Expose-Headers, "*");
// Access-Control-Expose-Headers
return fetchUtils.fetchJson(apiUrl, options)
};
export default {
// 获取列表
getList: async (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify(params.filter),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
const {status, headers, body, json } = await httpClient(url);
headers.set("a", "123")
console.log("status --->", status)
console.log("headers --->", headers)
console.log("body --->", body)
return ({
data: json,
total: 1,//parseInt(headers.get('x-total-count')),
});
},
getOne: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({
data: json,
})),
getMany: async (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
const { json } = await httpClient(url);
return ({ data: json });
},
getManyReference: async (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify({
...params.filter,
[params.target]: params.id,
}),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
const { headers, json } = await httpClient(url);
return ({
data: json,
total: parseInt(headers.get('content-range').split('/').pop(), 10),
});
},
update: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json })),
updateMany: async (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
const { json } = await httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'PUT',
body: JSON.stringify(params.data),
});
return ({ data: json });
},
create: (resource, params) =>
httpClient(`${apiUrl}/${resource}`, {
method: 'POST',
body: JSON.stringify(params.data),
}).then(({ json }) => ({
data: { ...params.data, id: json.id },
})),
delete: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'DELETE',
}).then(({ json }) => ({ data: json })),
deleteMany: async (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
const { json } = await httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'DELETE',
});
return ({ data: json });
}
};
I can't see the headers in browser console.
In the browser inspector the headers look like they are empty because it's an iterator, not an object. You can iterate through the response headers like so:
for (var pair of response.headers.entries()) {
console.log(pair[0]+ ': '+ pair[1]);
}
Check out this blogpost for more information: https://stevemiller.dev/2019/getting-response-headers-with-javascript-fetch/
I also had to explicitly expose my custom header in my web api backend for it to work. This might also be a reason why the response header is not showing up.

Getting multiple response while calling python API from Angular

While calling the python API from angular 10. I am getting multiple response. How to overcome it.
Python API Response from Angular Service call:
1. Object { type: 0 } # how to avoid this additional response
2. Object { headers: {…}, status: 201, statusText: "Created", url: "https://*********/1bcd2a20-dd60-4cfe-984f-7f3607f01e7c", ok: true, type: 4, body: "{\"status\":{}}" }
Angular Service Call:
postCall(identifier): Observable<HttpEvent<{}>> {
let token1 = localStorage.getItem('token');
const headerSettings: { [name: string]: string | string[]; } = {};
headerSettings['Authorization'] = 'Bearer ' + token1;
const req = new HttpRequest('GET', 'https://***********/'+identifier, null, {
reportProgress: false,
responseType: 'text'
});
const newHeader = new HttpHeaders(headerSettings);
let changedRequest = req.clone({
headers: newHeader
});
return this.http.request(changedRequest);
}

How is MultiValueDictKeyError and AttributeError related in Django here?

I have a function in Django that I am trying to solve from my previous question here. While trying out my own solutions, I have made significant updates but I encounter an error.
I'm trying this out right now:
def view_routes(request, query=None):
routes = None
if query is None:
routes = Route.objects.all()
else:
#View: Routes in Queried Boundary
if request.method == 'POST':
return HttpResponse("OK")
elif request.method == 'GET':
json_feature = json.loads(request.GET.get('geo_obj', False))
#json_feature = json.loads(request.GET['geo_obj'])
geom = make_geometry_from_feature(json_feature)
routes = Route.objects.filter(wkb_geometry__within=geom[0])
print("Total Routes Filtered: " + str(Route.objects.filter(wkb_geometry__within=geom[0]).count()))
#Render to Django View
routes_json = serialize('geojson', routes, fields=('route_type', 'route_long', 'route_id', 'wkb_geometry',))
routes_geojson = json.loads(routes_json)
routes_geojson.pop('crs', None)
routes_geojson = json.dumps(routes_geojson)
#return render(request, 'plexus/view_routes.html', {'routes':routes})
return redirect('routes_view', query)
I am having trouble switching/commenting out between these two lines:
json_feature = json.loads(request.GET.get('geo_obj', False))
json_feature = json.loads(request.GET['geo_obj'])
Both presents an error respectively:
TypeError: the JSON object must be str, not 'bool'
django.utils.datastructures.MultiValueDictKeyError: "'geo_obj'"
Edited function with AJAX inside:
function sendQueryData(url, query){
url =url.replace('query' , query);
if (query === ""){
alert("City Input Required");
}else{
if(geo_obj === null){
alert("Click Search Button...")
}else{
$.ajax({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
type: "GET",
url: url,
dataType: 'html',
data: {
'geo_obj' : JSON.stringify(geo_obj)
},
success: function(data){
alert(data);
window.location.href = url;
//var result = $('<div />').append(data).find('#list-group').html();
//$('#list-group').html(result);
},
error: function(xhr, textStatus, errorThrown) {
alert('Request Failed' + textStatus + ":" + errorThrown);
}
});
}
}
}
Try using json.loads(request.body) if you are passing raw JSON, request.GET['foo'] is for form-encoded data

error 405 get method not allowed

Angularjs code
var app = angular.module('myApp', []);
app.factory('httpSend', ['$http', '$q', function($http, $q) {
var app = {};
app.sendToServer = function(data) {
$http({
method: "POST",
url: '/report',
data: data,
headers: {
'Content-type': 'application/x-www-form.urlencoded;'
}
}).then(function(response) {
debugger
var result = data;
});
}
app.getfromServer = function() {
var def = $q.defer();
$http.get('/report').then(function(data) {
console.log(data);
def.resolve(data);
}),
function(error) {
def.reject("Failed to get albums");
};
return def.promise;
}
return app;
}]);
app.controller('myCtrl', ['$scope', '$http', 'httpSend', '$filter', function($scope, $http, httpSend, $filter) {
$scope.names = ["ankit patidar", "adhishi ahari", "kritin joshi", "kautilya bharadwaj", "punita ojha", "manvi agarwal", "apeksha purohit", "shipra jain", "mansi nangawat", "praveen soni"];
$scope.data = [];
$scope.names.forEach(function(name) {
$scope.data.push({
name: name,
checkin: "",
checkout: ""
})
});
$scope.login = [];
$scope.check = function(name, doing) {
debugger
name[doing] = new Date();
name[doing] = $filter('date')(name[doing], 'dd-MM-yyyy hh:mm:ss');
$scope.login.push(angular.copy(name));
if (doing == "checkout") {
var q = JSON.stringify($scope.login);
httpSend.sendToServer(q);
}
}
$scope.getData = function() {
httpSend.getfromServer();
}
}]);
`
Python Code
def get(self):
logging.info('get is triggered')
obj = CheckIn.query().fetch()
emp_obj = []
for x in obj:
logging.info('I am inside for loop ')
emp_obj.append({
'name': x.name,
'Check_in': x.inDate,
'check_out': x.outDate
})
logging.info('I am inside emp_obj')
self.response.write(json.dumps(emp_obj))
i need to fetch all the data stored on ndb datastore on front end view thats why i m using http get method but error is showed method not allowed. can u please help e despite using query fetch and showing its response on python ad triggering get method, why error is coming, is there a mistake in control flow or something is missing in my get method, as for now i m able to post nd store data
Change your factory to the following. Don't use the same variable app that you are using for initialising your module for your controller logic.
app.factory('httpSend',['$http', '$q',function($http, $q){
return {
'sendToServer': function(data) {
var def = $q.defer();
$http({
method: "POST",
url: '/report',
data: data,
headers: {
'Content-Type': 'application/json'
}
}).then(function(response) {
debugger
var result = response.data;
def.resolve(result );
});
return def.promise;
},
'getfromServer': function() {
var def = $q.defer();
$http.get('/report').then(function(data) {
console.log(data);
def.resolve(data);
}),
function(error) {
def.reject("Failed to get albums");
};
return def.promise;
}
}
}]);

Categories