FastAPI/Starlette middleware returns a StreamingResponse instead of JSONResponse - fastapi

I am trying to write a FastAPI middleware to add a header to a response. The contents of the header is based on the value of the response body. This is what I have so far:
from starlette.middleware.base import BaseHTTPMiddleware
class FooMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
response.headers["X-Foo"] = Foo(response.content)
return response
According to the docs, FastAPI returns JSON responses as a default (https://fastapi.tiangolo.com/advanced/custom-response/?h=stream#jsonresponse), and I have done nothing to override that.
The strange thing is that the type of the response object seems to be StreamingResponse (https://fastapi.tiangolo.com/advanced/custom-response/?h=stream#streamingresponse). What is causing that?

Related

Sending a Post Request from Ballerina

I want to send a post request using ballerina to get a access token from the Choreo Dev Portal. I am able to do it using postman. But unable to make it work in Ballerina code level. it gives 415 - unsupported media type error. Need some Help in Ballerina
import ballerina/http;
import ballerina/io;
import ballerina/url;
public function main() returns error? {
final http:Client clientEndpoint = check new ("https://sts.choreo.dev");
http:Request request = new();
string payload = string`grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=*******&
subject_token_type=urn:ietf:params:oauth:token-type:jwt&
requested_token_type=urn:ietf:params:oauth:token-type:jwt`;
string encodedPayload = check url:encode(payload, "UTF-8");
io:print(encodedPayload);
request.setTextPayload(encodedPayload);
request.addHeader("Authorization","Basic *****");
request.addHeader("Content-Type","application/x-www-form-urlencoded");
io:print(request.getTextPayload());
json resp = check clientEndpoint->post("/oauth2/token",request);
io:println(resp.toJsonString());
}
I was expecting an access token from Choreo Devportal for the particular application.
import ballerina/http;
import ballerina/io;
import ballerina/mime;
public function main() returns error? {
// Creates a new client with the backend URL.
final http:Client clientEndpoint = check new ("https://sts.choreo.dev");
json response = check clientEndpoint->post("/oauth2/token",
{
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"requested_token_type":"urn:ietf:params:oauth:token-type:jwt",
"subject_token":"****"
},
{
"Authorization": "Basic ****"
},
mime:APPLICATION_FORM_URLENCODED
);
io:println(response.toString());
}
This is the recommended way to send the post request with the form URL encoded payload.
Change the Content-type header setting method from addHeader() to setHeader().
The request.setTextPayload(encodedPayload); will set the Content-type as text/plain as the default content type header.
Later request.addHeader("Content-Type","application/x-www-form-urlencoded"); is executed. The addHeader() method will append the new value to the same header in addition to the previously added text/plain. But the setHeader() will replace the previously set header which is the correct way in this scenario.
However better way is to pass the Content-type as the second param of setXXXPayload() method.
request.setTextPayload(encodedPayload, "application/x-www-form-urlencoded");

How to add http headers to Firebase HttpsCallable function's response

Is it not possible to add headers to response? This code returns error when I add this header.
import * as functions from "firebase-functions";
export default functions
.https.onCall(async (_, context) => {
context.rawRequest.res?.setHeader(
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains"
);
return { message: "Hello World" };
});
{"error":{"message":"Bad Request","status":"INVALID_ARGUMENT"}}
Without header it works fine, but I need to add some headers to the response. How can I do it?
Thanks
It's not supported to change the details of the callable function protocol. The client and backend SDKs maintain full control of the HTTP request and response. You can only specify the JSON contents of the body in either direction.
If you need control over the headers, you should not use a callable function and instead write a normal HTTP function.

FastAPI: CORS Middleware not working with GET method

I try to use CORS on the FastAPi framework but it dose not working with GET method
Here's the code I'm working on:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=['*'],
allow_methods=["*"],
allow_headers=["*"],
)
#app.get("/test1")
async def test1():
return {"message": "Hello World"}
I had the same issue and the solution is to not use add_middelware but do the following:
First import from Starlette:
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
Create the middleware:
middleware = [
Middleware(
CORSMiddleware,
allow_origins=['*'],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*']
)
]
and then:
app = FastAPI(middleware=middleware)
This should work
Thanks #Sam_Ste, I had the same problem! I set my imports back to FastAPI and it still works. I think they are just proxies for the starlette modules (IMHO). The method is the vital thing, not using app_middleware.
from fastapi.middleware import Middleware
from fastapi.middleware.cors import CORSMiddleware
For me, none of the above mentioned ideas worked. I had to create a custom middleware like this.
#app.middleware("http")
async def cors_handler(request: Request, call_next):
response: Response = await call_next(request)
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Allow-Origin'] = os.environ.get('allowedOrigins')
response.headers['Access-Control-Allow-Methods'] = '*'
response.headers['Access-Control-Allow-Headers'] = '*'
return response
When testing, make sure you add Origin header to your request. Otherwise CORSMiddleware will not send back the cors headers.
It may not be clear at first, but it is written here in the documentation:
Simple requests
Any request with an Origin header. In this case the middleware will
pass the request through as normal, but will include appropriate CORS
headers on the response.
So any request without an Origin will be ignored by CORSMiddleware and no CORS headers will be added.

"Handler crashed with error runtime error: invalid memory address or nil pointer dereference", but POSTMAN is ok! Why this happens?

I work with vue and go for frontend and backend respectively. I send post request to my server and get 403 error code message(notAllowed). But in postman I get the objects and is fine.
Vue and Vuex
My axios post request:
const response = await this.$axios.post(`http://localhost:8000/v1/org/${params.organization}/kkms/${params.kkm}/closeShift`,{
headers : {
'token' : this.state.token.value
}});
I know I should also use other properties like 'Content-Type' and etc in headers, but know it works well with only "token" property in the other requests. I want to know whether problem in backend or frontend?
It seems you have a mistake in the axios request.
You are receiving a 403, that means you are not authorized (or sometimes something else, check the comments in the question and down here ).
As can be found in axios docs, the post request looks like this:
axios.post(url[, data[, config]]).
It accepts the config (so the headers) as THIRD parameter, while you are setting it as second parameter. Add an empty FormData object as second param, and just shift your config to the third param.
const fakeData = new FormData();
const response = await this.$axios.post(`http://localhost:8000/v1/org/${params.organization}/kkms/${params.kkm}/closeShift`,
fakeData,
{
headers : {
'token' : this.state.token.value
}
});

Unable to modify request in middleware using Scrapy

I am in the process of scraping public data regarding metheorology for a project (data science), and in order to effectively do that I need to change the proxy used on my scrapy requests in the event of a 403 response code.
For this, I have defined a download middleware to handle such situation, which is as follows
class ProxyMiddleware(object):
def process_response(self, request, response, spider):
if response.status == 403:
f = open("Proxies.txt")
proxy = random_line(f) # Just returns a random line from the file with a valid structure ("http://IP:port")
new_request = Request(url=request.url)
new_request.meta['proxy'] = proxy
spider.logger.info("[Response 403] Changed proxy to %s" % proxy)
return new_request
return response
After properly adding the class to settings.py, I expected this middleware to deal with 403 responses by generating a new request with the new proxy, hence finishing in a 200 response. The observed behaviour is that it actually gets executed (I can see the Logger info about Changed proxy), but the new request does not seem to be made. Instead, I'm getting this:
2018-12-26 23:33:19 [bot_2] INFO: [Response] Changed proxy to https://154.65.93.126:53281
2018-12-26 23:33:26 [bot_2] INFO: [Response] Changed proxy to https://176.196.84.138:51336
... indefinitely with random proxies, which makes me think that I'm still retrieving 403 errors and the proxy is not changing.
Reading the documentation, regarding process_response, it states:
(...) If it returns a Request object, the middleware chain is halted and the returned request is rescheduled to be downloaded in the future. This is the same behavior as if a request is returned from process_request().
Is it possible that "in the future" is not "right after it is returned"? How should I do to change the proxy for all requests from that moment on?
Scrapy will drop duplicate requests to the same url by default, so that's probably what's happening on your spider. To check if this is your case you can set this settings:
DUPEFILTER_DEBUG=True
LOG_LEVEL='DEBUG'
To solve this you should add dont_filter=True:
new_request = Request(url=request.url, dont_filter=True)
Try this:
class ProxyMiddleware(object):
def process_response(self, request, response, spider):
if response.status == 403:
f = open("Proxies.txt")
proxy = random_line(f)
new_request = Request(url=request.url)
new_request.meta['proxy'] = proxy
spider.logger.info("[Response 403] Changed proxy to %s" % proxy)
return new_request
else:
return response
A better approach would be to use scrapy random proxies module instead:
'DOWNLOADER_MIDDLEWARES' : {
'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
'rotating_proxies.middlewares.BanDetectionMiddleware': 620
},

Resources