Django Rest Framework Cors issues with query params - javascript

When I make a GET request without queryparams my backend provides the data no problem, but if I pass any queryparams the request gets rejected.
[Example of Rejection in network tab, status column says CORS ERROR][1]
The fetch being made is this (it's within a react useEffect)
fetch('http://localhost:8000/gems/' + new URLSearchParams({
filters: 'all'
}))
If I remove the + params it works just fine, but removing the trailing slash also causes the issue.
in package.json I have added this
"proxy": "https://localhost:8000/",
The backend view is this (going to overwrite get_queryset to read the params)
class GemViewSet(viewsets.ModelViewSet):
queryset = Gem.objects.all()
serializer_class = GemSerializer
and I've installed the cors app, added the middleware and set
CORS_ALLOW_ALL_ORIGINS = True
If someone could set me on the right track I'd be very grateful.
[1]: https://i.stack.imgur.com/g0dIp.png

Related

Data posted to flask endpoint from JS not processed in endpoint

I have written a simple todo app with react acting as a frontend and flask handling CRUD from a DB. The app is using axios to handle the requests; GET completes fine however when attempting to POST JSON the flask api returns a 400 error. Here's some condensed sample code.
JS POST function.
function testPost(){
axios.post('http://'+window.location.hostname+':8000/todo/', {
title: "test123",
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
}
Serverside
class Todo(Resource):
def post(self): # create a new todo
conn = pool.getconn()
cur = conn.cursor()
app.logger.info(request.form['title'])
cur.execute("INSERT INTO todo (task, done) VALUES (%s, %s)", (request.form['title'], False))
conn.commit()
app.logger.error(e)
cur.close()
pool.putconn(conn)
Other methods not shown
Then the rest of the server code attaching the resource to the api and the CORS setup (not shown in file order)
app = Flask(__name__)
CORS(app, methods=['POST','GET','PUT','DELETE'])
api = Api(app)
api.add_resource(Todo, '/todo/')
app.run(debug = True, host='0.0.0.0', port=port)
Tests
Using python to test the api works fine, running this in a seperate python file will add to the DB.
response = requests.post(URL + "todo/", data={"title": f"test{randint(1, 100)}"})
My best guess is that axios is not adding the data to the request in a way that the backend is unable to process. Before using axios I tried to make the request with XMLHttprequest however this presented the same problem. I swapped to axios on the recommendation of someone else, given its alleged improved simplicity.
request.form['key'] and request.get_json()['key'] are completely different fields python requests in the way I used it posts to the former and js posts to the latter. Modifying the function to use whichever is available fixes this.

"redirect_uri_mismatch" when sending authentication code to GoogleAPI

I am having trouble with the authentication process for the GoogleAPI. In the end I want to be able to read the users steps using the GoogleFit API and then store that value in a database. Currently I'm using restdb.io and executing javascript in codehooks.
The documentation from Google that I am following can be found here, clicking on the HTTP/REST option in the code examples. At the moment I am at step 5: I have gotten the users authentication code and stored it in the database. Now I have to POST the code along with some other parameters and get the access and refresh tokens.
If the POST is successful (from what I understand) I should get back a 200-OK message that the request was valid. Google will then POST a JSON body with the access and refresh token to the redirect_uri that I have specified in my GoogleAPI credentials page and the initial request. At redirect_uri I have to handle the request and save the two values.
The problem is that I receive a redirect_uri_mismatch - Bad Request message from Google as a response when executing the request. I get it at the log.debug("ERROR HERE: " + [...]); in the code below:
async function mainFunction(){
const authCode = THIS_IS_MY_AUTHENTICATION_CODE;
try {
var answer = await postRequestToGoogle(authCode);
//do stuff with response from Google
} catch (error) {
//do stuff
}
}
async function postRequestToGoogle(authCode){
//body for the request
const params = "code=" + authCode + "&" +
"client_id=THIS_IS_MY_CLIENT_ID" + "&" +
"client_secret=THIS_IS_MY_CLIENT_SECRET" + "&" +
"redirect_uri=THIS_IS_MY_REDIRECT_URI" + "&" +
"grant_type=authorization_code";
try{
const result = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: params})
.then(res => {
log.debug("ERROR HERE: " + JSON.stringify(res.json()));
return res.json();
})
//return JSON back main function
return result;
}catch(error){
//do stuff
}
}
I looked up the error message and tried some things:
Copy and pasted multiple different Authorized redirect URI from the GoogleAPI credentials page into the code to make sure that there is no problem with
http/https
www/no www
trailing slashes
typos or capitalization
Waited for changes to be processed by Google (read that it can more than 30min)
Changed all the other parameters to see if the redirect_uri is actually the problem
If code is changed the message is invalid_grant - Bad Request
If client_id is changed the message is invalid_client - The OAuth client was not found
If client_secret is changed the message is invalid_client - Unauthorized
If the grant_type is changed the message is unsupported_grant_type - Invalid grant_type
That's why I think the issue is the redirect_uri, but it's unclear to me how since I copy&pasted it. Something that came to mind was that maybe the value of redirect_uri gets changed when it's read by Google? Or maybe when the request is being put together? Do some characters have to be replaced?
I tried to analyze the request with Wireshark but didn't think about the fact that it's HTTPS so I would have I would have to decrypt it.. Is that something I should look into?
Thank you for taking the time to read all of this! If you have any advice please let me know :)
Update 16.11.20:
I have created a new OAuth 2.0 Client ID and used the new id/secret in my request. The resulting message the same as before. I will wait and try again tomorrow to see if maybe Google needs some more time. Then I'll try to delete all current IDs and start with a fresh GoogleAPI project.
Update 19.11.20:
Creating a new OAuth 2.0 Client ID did not resolve my problem, neither did creating a whole new GoogleAPI project and adding those credentials into the request. I am in contact with the developers of restdb.io and have asked them to add the Google Auth Library: Node.js Client to the list of supported Node.js packages. Hopefully that will help, I will give it a try as soon as it can be used :)
Update 02.12.20:
No progress so far, but I'm optimistic that the developers will add the package soon. I will post a final update as soon as I am done with this project.

Swagger UI always shows up in NestJS

I am currently learning my way around in NestJS. Now I am experimenting with the Swagger feature. I followed the explanations on the NestJS site and I am getting a nice Swagger page displayed. However, now I am no longer able to use my controller pathes.
Example:
I have a path /api/users that will return a list of user records. After adding the Swagger feature I get the Swagger UI on /api. When I try to request /api/users I also get the swagger UI, this time empty.
When I click the "Try it out" button for the "user" API /users instead of /api/users will be executed, of course with a 404 response.
What am I doing wrong? Please help.
I'm assuming you have set the global prefix of your app to /api. Then you also have to set the base path for swagger accordingly. Also, you should mount your swagger docs to an unused path:
// Set the global prefix for all controllers to /api
app.setGlobalPrefix('api');
const document = SwaggerModule.createDocument(
app,
new DocumentBuilder()
// Set the base path for swagger accordingly
.setBasePath('api')
.build(),
);
// Choose an unused path to mount your swagger module
SwaggerModule.setup('docs', app, document);
// ^^^^^^
I was able to achieve this by below code:
app.setGlobalPrefix("api");
setupSwagger(app);
app.setGlobalPrefix("");
setupSwagger definition:
export const setupSwagger = (app: INestApplication) => {
const options = new DocumentBuilder()
.setTitle(SWAGGER_API_NAME)
.setDescription(SWAGGER_API_DESCRIPTION)
.setVersion(SWAGGER_API_CURRENT_VERSION)
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup(SWAGGER_API_ROOT, app, document);
};

Network error: Unexpected token < in JSON at position 0 at new ApolloError

const httpLink = createHttpLink({
uri: 'http://localhost:3090/'
})
const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache()
})
client.query({
query: gql`
query users {
email
}
`,
})
.then(data => console.log(data))
.catch(error => console.error(error));
This query gives an error when fetching from client-side code but when i execute this query in browser on http://localhost:3090/graphql it fetches data correctly
The graphql endpoint you are posting your queries to is missing the /graphql. So your server probably returns an html document containing the 404 error message that starts with < from <html.... Apollo tries to parse that as the query result and fails to do so.
Check that httpLink is actually localhost:3090/graphql.
Also the syntax of a query is either:
{
users {
email
}
}
or if you want to name the query:
query Users {
users {
email
}
}
For posterity in case someone finds this in the future, another reason you might get this error is if your API is returning something other than JSON.
https://medium.com/programmers-developers/one-simple-apollo-client-debugging-tip-youll-like-7877a97b9c16
I ran into this issue because the content type that was being returned from my API was text/plain rather than application/json. Apollo lets you specify a different body serializer in this case.
https://www.apollographql.com/docs/link/links/rest/
This happens when you do not define the path correctly in the queries files. This is a typechecking error when passing arguments
In my case this error was being thrown due to a trailing slash I had after the API URL. A possible fix would be checking your API URL properly and ensuring it is referencing the correct URL.
In my case, I only had to take out the trailing slash / after the URL.
As #Trixn posted: "So your server probably returns an html document containing the 404 error message that starts with < from <html.... Apollo tries to parse that as the query result and fails to do so."
In my case, I did not put "http://" in the beginning of my URL, and this returned a HTML web page with an error message.
Also check that usually, you should use "https://"
Make sure that you have configured the uri for httpLink and wsLink same.
I have seen this error when I had different uris as below.
httpLink had uri : "http://localhost:4000"
wsLink had uri: "ws://localhost:4000/graphql"
setting httpLink's uri to http://localhost:4000/graphql helped resolving the issue.
In my case, I recieved the same error, when I switched from a create-react-app to a next JS app pointing to port 3000.The react app had some values saved to local storage (tokens). In my case all I had to do, was to clear the local storage for localhost:3000, and I got rid of the error.
In my case, there was a typo in my graphql endpoint url. I had added a few characters to the end by accident. I think it would be good practice to add a catch rule for this situation so it does not break the site, just does not show the content and posts error to console log.

Avoid new axios request each time vue component is rendered

I'm very much new to Vue so please excuse my lack of knowledge here.
In one of my (child) components (Product_Collection.vue) I make an axios request to get some products from my Shopify store via their GraphQL API:
data: () => {
return {
products: null
}
},
methods: {
getProducts: function(){
// 1. axios code here...
// 2. ...then assign result to "this.products" data property
}
}
The products are displayed as thumbnails (let's say there's 10 t-shirts). I then click a product to view it in more detail with more product info and images etc (very much an Amazon-like experience).
The product is shown on it's own in a Product_Single.vue component. So far so good.
But here's the problem...
When I click back to the products page (Product_Collection.vue) (the page with the 10 t-shirts on) the axios request to the Shopify API gets called again and the component is re-rendered from scratch.
My question is how do I tell Vue to stop fetching the data from the Shopify API each time the component is rendered? Thank you
Sounds like you want cache-control, which Axios supports with an extension.
https://github.com/kuitos/axios-extensions
import axios from 'axios';
import { cacheAdapterEnhancer } from 'axios-extensions';
const http = axios.create({
baseURL: '/',
headers: { 'Cache-Control': 'no-cache' },
// cache will be enabled by default
adapter: cacheAdapterEnhancer(axios.defaults.adapter)
});
http.get('/users'); // make real http request
http.get('/users'); // use the response from the cache of previous request, without real http request made
http.get('/users', { cache: false }); // disable cache manually and the the real http request invoked
Or you could go a more complex route and cache the results yourself into an object, and check if that object exists, but then you'll have to deal with when you want to expire the data in that object.
If you use a cache-control, and cache-control headers and expiry, you can drive all that from the API layer and not have to deal with manually managed stale data in the application.
If you are not using Vue Router, and hiding the list page, you can try a simple option.
Instead of v-if, use v-show. The component will be hidden and displayed again. It won't be re-created.
Also, where do you call the API? From created or mounted hook?
If You are using GraphQL then there is the Apollo state management way which integrates nicely with graphQL. Check this out: https://dev.to/n_tepluhina/apollo-state-management-in-vue-application-8k0
So instead of rewriting the app with axios and adding vuex in the mixture, maybe Apollo client would be more convenient

Categories

Resources