react-admin fetchUtils doesn't get headers - python

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.

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,
);

FLASK request.json not working with React payload

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.

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);
}

react-table not rendering server-side data in table (with manual pagination, filtering & sorting)

I'm having trouble getting data from the backend (Python API) to show in react-table manually. I've read the documentation and I'm trying to use the example here: https://react-table.js.org/#/story/server-side-data
I'm only seeing data in one column and only for 6 records which is really weird. It's probably the way I'm mixing in async/await syntax with the example code which uses a promise. I was able to create a simple react-table fetching data with the same async/await syntax, but when I added the server-side data code from the example (the requestData function) it wouldn't work.
I've spent days on this and looking all over Stackoverflow and the internet. I'm a newbie so please go easy on me. Here's what I have:
import React from 'react'
import { render } from 'react-dom'
import ReactTable from 'react-table'
import api from 'src/api'
import { orderBy } from 'lodash'
// importing react-table css would not work so I added it using cdn link
const requestData = async (pageSize, page, sorted, filtered) => {
// api is a wrapper for axios.create()
const rawData = await api.admin.exercise.feed()
return new Promise((resolve, reject) => {
let filteredData = rawData;
if (filtered.length) {
filteredData = filtered.reduce((filteredSoFar, nextFilter) => {
return filteredSoFar.filter(row => {
return (row[nextFilter.id] + "").includes(nextFilter.value);
});
}, filteredData);
}
const sortedData = orderBy(
filteredData,
sorted.map(sort => {
return row => {
if (row[sort.id] === null || row[sort.id] === undefined) {
return -Infinity;
}
return typeof row[sort.id] === "string"
? row[sort.id].toLowerCase()
: row[sort.id];
};
}),
sorted.map(d => (d.desc ? "desc" : "asc"))
);
const res = {
rows: sortedData.slice(pageSize * page, pageSize * page + pageSize),
pages: Math.ceil(filteredData.length / pageSize)
};
resolve(res);
});
};
export class ExerciseList extends React.Component {
constructor() {
super();
this.state = {
data: [],
pages: null,
loading: true
};
this.fetchData = this.fetchData.bind(this);
}
setLoading(loading) {
this.setState({ loading })
}
fetchData(state, instance) {
this.setLoading(true);
requestData(
state.pageSize,
state.page,
state.sorted,
state.filtered
).then(res => {
this.setState({
data: res.rows,
pages: res.pages,
loading: false
});
});
}
render() {
const { data, pages, loading } = this.state;
return (
<div>
<ReactTable
columns={[
{
Header: "Name",
accessor: "name"
},
{
Header: "Movement",
accessor: "movement"
},
{
Header: "Equipment",
accessor: "equipments"
},
{
Header: "Channel",
accessor: "channel"
},
{
Header: "Level",
accessor: "skill_level"
},
{
Header: "Duration",
accessor: "duration",
filterable: false
},
{
Header: "Injuries",
accessor: "injuries"
},
{
Header: "Is Substitute",
accessor: "has_video",
Cell: ({ value }) => (value? 'Yes': 'No'),
filterable: false
}
]}
data={data}
pages={pages}
loading={loading}
onFetchData={this.fetchData}
manual
filterable
defaultPageSize={10}
className="-striped -highlight"
/>
</div>
);
}
}
render(<ExerciseList />, document.getElementById('datatable'));
Please refer the link for server-side sorting, pagination and manual filtering within the grid
// Component related to methods for sorting, pagination server side and filtering manual filtering with in the grid
import React from 'react'
import 'react-table/react-table.css'
import ReactTable from 'react-table'
import autoBind from 'react-autobind'
import {filterCaseInsensitive} from '../../helper/commonMethods'
class ServerSideAtomGrid extends React.Component {
super(props)
const userDetails = getUserDetails()
this.state = {
page: 0,
pageSizeOptions: [500, 1000, 2000, 4000],
pageSize: 500,
totalRecords: 0,
nextCursor: '*',
cursorList: [{
page: 0,
cursor: '*'
}],
sortFields: {
field: 'created_dtm',
sort: 'desc'
},
columnData,
}
autoBind(this)
}
handlePageChange (page) {
const pageNumber = (page)
const cursorList = this.state.cursorList
let cusrsorMark = ''
_.each(cursorList, (list) => {
if (list.page === pageNumber) {
cusrsorMark = list.cursor
}
})
this.setState({
nextCursor: cusrsorMark,
page: pageNumber
}, () => this.searchData(cusrsorMark, pageNumber))
}
handleSizePerPageChange (pageSize) {
this.resetData(pageSize)
this.searchData('*', 0)
}
handleSorting = (state, instance) => {
const sorted = state
let field = 'created_dtm'
let sort = 'desc'
sorted && sorted.length > 0 && sorted.map(fld => {
field = fld.id
sort = fld.desc ? 'desc' : 'asc'
})
this.setState({
sortFields: {
field,
sort
}
}, () => this.searchData('*', 0))
}
////
searchData('*', 0) {
//Axios call you cna have
}
filterCaseInsensitive (filter, row) {
const id = filter.pivotId || filter.id
return row[id] ? row[id].toString().toLowerCase().includes(filter.value.toLowerCase()) : true
}
render () {
const {
classes, gridData, gridColumns, defaultFilter, totalRecords,
gridPageSizeOptions, gridPage, gridPages, gridPageSize, gridLoading
} = this.props
return (
<div>
<ReactTable
columns={gridColumns}
data={gridData}
onSortedChange={(state, instance) => {
this.handleSorting(state, instance)
}}
filterable={defaultFilter}
defaultFilterMethod={filterCaseInsensitive}
noDataText="Ops No result found!"
defaultPageSize={this.state.pageSize}
className="-highlight"
style={{height: `${totalRecords < 25 ? '' : `800px`}`, width: '100%', textAlign: 'center'}}
pageText={`Total Count : ${totalRecords.toLocaleString()} Page: `}
loading={gridLoading}
page={this.state.page}
pages={this.state.pages}
showPaginationTop
pageSize={this.state.pageSize}
pageSizeOptions={gthis.state.pageSizeOptions}
minRows={25}
manual
onPageChange={page => {
this.setState({page})
this.handlePageChange(page)
}}
onPageSizeChange={(pageSize, page) => {
this.setState({
page,
pageSize
})
this.props.handleSizePerPageChange(pageSize)
}}
showPageJump={false}
/>
</div>
)
}
}
export default (ServerSideAtomGrid)
My Fiddle: https://jsfiddle.net/gowthamguruju/o9ybxqaj/8/

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