Fastapi how to add variable to all router with TemplateResponse - fastapi

I use JinjaTemplate and return data to html this way
#router.get('/', response_class=HTMLResponse)
async def main_page(request: Request,
members: List = [e.value for e in MembersQuantity],
activity_service: ActivityService = Depends(),
):
activity = await activity_service.get()
return templates.TemplateResponse('base.html',
context={
'request': request,
'members': members,
'activities': activity,
}
)
There are dozens of such routs in the project. And each of them needs to have the same context variable. I need to pass variables in all the routers - for example user.
I can write this way of passing the user object (it store in Redis)
#router.get('/', response_class=HTMLResponse)
async def main_page(request: Request,
members: List = [e.value for e in MembersQuantity],
activity_service: ActivityService = Depends(),
):
activity = await activity_service.get()
user = None
if await is_authenticated(request):
user = await get_current_user(request)
return templates.TemplateResponse('base.html',
context={
'request': request,
'members': members,
'activities': activity,
'user': user,
}
)
template
<html>
<head>
<title>Item Details</title>
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet">
</head>
<body>
<a class="" href="{{ url_for('user_notification', pk=user.user_id) }}">
</body>
</html>
But doing it in every router is completely wrong.
I know of different ways to pass global variables using middleware in request.state or response.set_cookie. But this methods are not suitable for security reasons.
How can I globally transfer the value I want to the context for all the routs?

Related

How to send RedirectResponse from a POST to a GET route in FastAPI?

I want to send data from app.post() to app.get() using RedirectResponse.
#app.get('/', response_class=HTMLResponse, name='homepage')
async def get_main_data(request: Request,
msg: Optional[str] = None,
result: Optional[str] = None):
if msg:
response = templates.TemplateResponse('home.html', {'request': request, 'msg': msg})
elif result:
response = templates.TemplateResponse('home.html', {'request': request, 'result': result})
else:
response = templates.TemplateResponse('home.html', {'request': request})
return response
#app.post('/', response_model=FormData, name='homepage_post')
async def post_main_data(request: Request,
file: FormData = Depends(FormData.as_form)):
if condition:
......
......
return RedirectResponse(request.url_for('homepage', **{'result': str(trans)}), status_code=status.HTTP_302_FOUND)
return RedirectResponse(request.url_for('homepage', **{'msg': str(err)}), status_code=status.HTTP_302_FOUND)
How do I send result or msg via RedirectResponse, url_for() to app.get()?
Is there a way to hide the data in the URL either as path parameter or query parameter? How do I achieve this?
I am getting the error starlette.routing.NoMatchFound: No route exists for name "homepage" and params "result". when trying this way.
Update:
I tried the below:
return RedirectResponse(app.url_path_for(name='homepage')
+ '?result=' + str(trans),
status_code=status.HTTP_303_SEE_OTHER)
The above works, but it works by sending the param as query param, i.e., the URL looks like this localhost:8000/?result=hello. Is there any way to do the same thing but without showing it in the URL?
For redirecting from a POST to a GET method, please have a look at this and this answer on how to do that and the reason for using status_code=status.HTTP_303_SEE_OTHER (example is given below).
As for the reason for getting starlette.routing.NoMatchFound error, this is because request.url_for() receives path parameters, not query parameters. Your msg and result parameters are query ones; hence, the error.
A solution would be to use a CustomURLProcessor, as suggested in this and this answer, allowing you to pass both path (if need to) and query parameters to the url_for() function and obtain the URL. As for hiding the path and/or query parameters from the URL, you can use a similar approach to this answer that uses history.pushState() (or history.replaceState()) to replace the URL in the browser's address bar.
Complete working example can be found below (you can use your own TemplateResponse in the place of HTMLResponse).
from fastapi import FastAPI, Request, status
from fastapi.responses import RedirectResponse, HTMLResponse
from typing import Optional
import urllib
app = FastAPI()
class CustomURLProcessor:
def __init__(self):
self.path = ""
self.request = None
def url_for(self, request: Request, name: str, **params: str):
self.path = request.url_for(name, **params)
self.request = request
return self
def include_query_params(self, **params: str):
parsed = list(urllib.parse.urlparse(self.path))
parsed[4] = urllib.parse.urlencode(params)
return urllib.parse.urlunparse(parsed)
#app.get('/', response_class=HTMLResponse)
def event_msg(request: Request, msg: Optional[str] = None):
if msg:
html_content = """
<html>
<head>
<script>
window.history.pushState('', '', "/");
</script>
</head>
<body>
<h1>""" + msg + """</h1>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
else:
html_content = """
<html>
<body>
<h1>Create an event</h1>
<form method="POST" action="/">
<input type="submit" value="Create Event">
</form>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
#app.post('/')
def event_create(request: Request):
redirect_url = CustomURLProcessor().url_for(request, 'event_msg').include_query_params(msg="Succesfully created!")
return RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
Update
Regarding adding query params to url_for(), another solution would be using Starlette's starlette.datastructures.URL, which now provides a method to include_query_params. Example:
from starlette.datastructures import URL
redirect_url = URL(request.url_for('event_msg')).include_query_params(msg="Succesfully created!")
return RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)

NextJS - Sensitive Provider only in specific pages

I have a question regarding the usage of Contexts in a Next.js application.
I wrote one Context (called WalletContext) which loads/stores data from one protected API.
export function WalletProvider({ children }: WalletProviderProps) {
const [transactions, setTransactions] = useState<Transactions[]>([]);
useEffect(() => {
// fetch data from API (using JWT)
setTransactions(...);
}, []);
return (
<WalletContext.Provider value={{ transactions }}>
{children}
</WalletContext.Provider>
);
}
export const WalletContext = createContext({} as WalletContextData);
export const useWallet = () => useContext(WalletContext);
To use this context, I found two approaches:
Approach 1: surround the entire App
File _app.tsx:
<WalletProvider>
<Component {...pageProps} />
</WalletProvider>
Pros:
only one API request is made when navigating between protected pages (the state persists).
Cons:
exposing the Provider for non-authenticated users, in non-protected pages
having to add if(s) inside the provider to check the user is authenticated in order to decide if it is necessary to call the API or not
Approach 2: not surround the App, but only the specific pages or components
File protected-page-1.tsx:
<WalletProvider>
<Page1 />
</WalletProvider>
File protected-page-2.tsx:
<WalletProvider>
<Page2 />
</WalletProvider>
File public-page-3.tsx:
<Page3 />
Pros:
exposing the Provider only for authenticated users
not having to add if(s) to decide if the API should be requested or not
Cons:
every time the user navigates from one protected page to another protected page, the API request is performed again (losing the context state)
Am I missing something? Is there a better way to handle this?

Fetch with Session ID in ember JS

i am a newbie in ember and servlet. Here is the code snippet of dashboard.js from the route directory.
import Route from '#ember/routing/route';
export default class DashboardRoute extends Route {
async model()
{
let response = await fetch("http://localhost:8080/Servlet/getfilename");
let data = await response.json();
return data;
}
}
I have maintained Session in my Servlet (i.e) When an user logged in via login Page LoginServlet will create a Session for the request and redirects the user to the Dashboard Page, where it fetches a json response from another servlet which does a session validation. Now that throws an error saying that the Session is null.Why does this happen? Is there is way to store the Session ID in ember and send it to the servlet via fetch?
Thanks in advance
I think you'll want to look over these docs from MDN: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
potentially, adding this to your fetch request's options:
credentials: 'same-origin',
more information on credentials can be found here: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials

ASP.NET MVC Stripe Error No such token

I cant seem to solve this error
Credit.cshtml
<form action="#Url.Action("Charge", "Home")" method="POST">
<article>
<label>Amount: $5.00</label>
</article>
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_6pRNASCoBOKtIshFeQd4XMUh"
data-amount="1000"
data-name="My Project Name"
data-description="Premium Account (€10)"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto"
data-zip-code="true"
data-currency="eur">
</script>
</form>
HomeController
[HttpPost]
//Parameters can contain stripeToken, stripeEmail, stripeName, stripeAddress submitted from Credit.cshtml form
public ActionResult Charge(string stripeToken, string stripeEmail)
{
Debug.WriteLine("stripe token is " + stripeToken);
Debug.WriteLine("stripe email is " + stripeEmail);
StripeConfiguration.SetApiKey("<!-- my secret key -->");
//Take the token submitted by the form
var token = stripeToken;
//Charge to the user card
var charges = new StripeChargeService();
var charge = charges.Create(new StripeChargeCreateOptions
{
Amount = 1000,
Currency = "sgd",
Description = "Example charge",
SourceTokenOrExistingSourceId = token
});
return View();
}
An exception of type 'Stripe.StripeException' occurred in Stripe.net.dll but was not handled in user code
No such token: tok_1BYf3F2eZvKYlo2C0MJgUKKD
What is happening? Some help pleasee thankss
A "no such token" error generally happens when the token you are trying to charge does not match the account you are making the API call on.
I'd recommend double checking the API keys that you're using, make sure the publishable key on your front-end and secret key in your back-end belong to the same account.
If switching up API Keys doesn't resolve it, you'd want to get in touch with Stripe, their support can look up the token id and tell you why it's not working!

Firebase Authentication

Not sure why this code isn't running. Copied all required snippets from Firebase and my button is firing. Any ideas as to what could be going wrong? Github is making me write more stuff in here so here.
Thanks!
<head>
<meta charset=utf-8 />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Auth</title>
<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-database.js"></script>
</head>
<body>
<button onclick="googleAuth()">SIGN UP</button>
<script>
var provider = new firebase.auth.GoogleAuthProvider();
var googleAuth = function() {
console.log(firebase);
firebase.auth().signInWithPopup(provider).then(function(result) {
// This gives you a Google Access Token. You can use it to access the Google API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
}).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
});
};
</script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyBeJIaAAD5-vK0kBDTRH6Y6_1ByVE3erJY",
authDomain: "fir-chat-d28be.firebaseapp.com",
databaseURL: "https://fir-chat-d28be.firebaseio.com",
storageBucket: "fir-chat-d28be.appspot.com",
};
firebase.initializeApp(config);
</script>
</body>
You must follow the instructions in log:
The current domain is not authorized for OAuth operations. This will prevent signInWithPopup, signInWithRedirect, linkWithPopup and linkWithRedirect from working. Add your domain to the OAuth redirect domains list in the Firebase console -> Auth section -> Sign in method tab.
See how it says signInWithPopup will not work without your domain added in Firebase console
Follow the instructions I listed when I was having a similar error, this will enable you to solve the oAuth issue.
Firebase GAS webapp Google popup disappears
Your code looks good, but if you run that as an HTML file, you'll get an error similar to "This operation is not supported in the environment this application is running on. "location.protocol" must be http or https".
I'd suggest you to take advantage of Firebase Hosting feature. You can easily deploy and host your app's static assets like HTML, CSS, JavaScript, etc.. Moreover, it's free of charge.
in my experience ( just for advice ), dont use firebase auth with provider like google or facebook. use email and password auth for better security. you can use FirebaseUi, verry easy to understand, and you can copy-paste if you like. if you use Google auth, every user who have google account can access your apps, read, write, delete, and update. except, you have better experience with Firebase Rules.

Resources