I am getting encoded data when I am downloading the image/file - express

I have set up a proxy in my NestJs Application to bypass the CORS restrictions.
The EP for the service is -
https://api.niftyuat.com/cors?cd=https://google.com
And it will give me the body of the google home page hence bypassing the CORS restriction.
But the issue is, when I try to download any image or some file, it gives me some kind of encoded input.
https://api.niftyuat.com/cors?cd=https://niftypm-production-assets-user.s3-accelerate.amazonaws.com/a895af03-aa9e-4b60-a3b4-54776b240c3e-Screen%20Shot%202021-03-18%20at%2012.03.24%20PM.png
I am also sharing my code.
#Controller('cors')
export class CorsController {
constructor(private readonly corsService: CorsService) { }
#Get()
async get(#Query('cd') uri: string, #Response() res: ExpressResponse) {
try {
let url = uri.charAt(0) == '/' ? uri.substring(1) : uri;
const response: AxiosResponse = await axios.get(url, {
timeout: 5000,
})
res.writeHead(200, { ...response.headers })
res.write(JSON.stringify(response.data))
res.end()
} catch (error) {
return error.message
}
}
}
Any idea what I am doing wrong here?

You forgot to set Content-Type header in response. Set it to 'image/png' or 'image/x-png'.

Related

Axios interceptors don't send data to API in production Heroku app

This is part 2 of me debugging my application in production
In part 1, I managed to at least see what was causing my problem and managed to solve that.
When I send a request to my API which is hosted on Heroku using axios interceptor, every single request object looks like this in the API
{ 'object Object': '' }
Before sending out data to the API, I console.log() the transformRequest in axios and I can see that the data I am sending is actually there.
Note: I have tested this process simply using
axios.<HTTP_METHOD>('my/path', myData)
// ACTUAL EXAMPLE
await axios.post(
`${process.env.VUE_APP_BASE_URL}/auth/login`,
userToLogin
);
and everything works and I get data back from the server.
While that is great and all, I would like to abstract my request implementation into a separate class like I did below.
Does anyone know why the interceptor is causing this issue? Am I misusing it?
request.ts
import axios from "axios";
import { Message } from "element-ui";
import logger from "#/plugins/logger";
import { UsersModule } from "#/store/modules/users";
const DEBUG = process.env.NODE_ENV === "development";
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
timeout: 5000,
transformRequest: [function (data) {
console.log('data', data)
return data;
}],
});
service.interceptors.request.use(
config => {
if (DEBUG) {
logger.request({
method: config.method,
url: config.url
});
}
return config;
},
error => {
return Promise.reject(error);
}
);
service.interceptors.response.use(
response => {
console.log('axios interception response', response)
return response.data;
},
error => {
const { response } = error;
console.error('axios interception error', error)
if (DEBUG) {
logger.error(response.data.message, response);
}
Message({
message: `Error: ${response.data.message}`,
type: "error",
duration: 5 * 1000
});
return Promise.reject({ ...error });
}
);
export default service;
Login.vue
/**
* Sign user in
*/
async onClickLogin() {
const userToLogin = {
username: this.loginForm.username,
password: this.loginForm.password
};
try {
const res = await UsersModule.LOGIN_USER(userToLogin);
console.log("res", res);
this.onClickLoginSuccess();
} catch (error) {
throw new Error(error);
}
}
UsersModule (VUEX Store)
#Action({ rawError: true })
async [LOGIN_USER](params: UserSubmitLogin) {
const response: any = await login(params);
console.log('response in VUEX', response)
if (typeof response !== "undefined") {
const { accessToken, username, name, uid } = response;
setToken(accessToken);
this.SET_UID(uid);
this.SET_TOKEN(accessToken);
this.SET_USERNAME(username);
this.SET_NAME(name);
}
}
users api class
export const login = async (data: UserSubmitLogin) => {
return await request({
url: "/auth/login",
method: "post",
data
});
};
I'm not sure what you're trying to do with transformRequest but that probably isn't what you want.
A quote from the documentation, https://github.com/axios/axios#request-config:
The last function in the array must return a string or an instance of Buffer, ArrayBuffer, FormData or Stream
If you just return a normal JavaScript object instead it will be mangled in the way you've observed.
transformRequest is responsible for taking the data value and converting it into something that can actually be sent over the wire. The default implementation does quite a lot of work manipulating the data and setting relevant headers, in particular Content-Type. See:
https://github.com/axios/axios/blob/885ada6d9b87801a57fe1d19f57304c315703079/lib/defaults.js#L31
If you specify your own transformRequest then you are replacing that default, so none of that stuff will happen automatically.
Without knowing what you're trying to do it's difficult to advise further but you should probably use a request interceptor rather than transformRequest for whatever it is you're trying to do.

Capture a Response from GET and Use it in the Next Request

I am trying to use the response of axios.get, and use it in axios.post. How can I use the response as a header in the POST request?
I tried using axios.post with headers defined in the request config:
var config = {
headers: {
'Access-Control-Allow-Origin': '*',
'user': newUser.eid,
'pass':'bd957c3fbb'
}
}
/*
const axios = require('axios')
getCrumb() {
return axios.get('https://jenkins.com/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)', config)
.then(response => {
return response
})
}
*/
/* code to get jenkins crumb */
const getJenkinsCrumb = () => {
try {
return axios.get('https://jenkins.com/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)', config)
.then((crumbValue) => {
console.log(crumbValue.data);
})
} catch (error) {
console.log(error)
}
}
getJenkinsCrumb();
I want use the response from the previous GET request (above) as a header in the POST call (below).
var crumbHeader = {
headers: {
'Access-Control-Allow-Origin': '*',
}
}
/* post api to kick off the build */
try {
return axios.post('https://abc123:bd95701859#jenkins.com/job/Non- PAR/job/Non-Prod-Jobs/job/uitest/job/TestJob/buildWithParameters?nodes=100000&clustername=clustername', crumbHeader)
.then((postKickTest) =>{
console.log(postKickTest.data);
})
} catch (error) {
console.log(error)
}
The Axios request config includes a headers property to specify the request's headers. The config can be specified as the 2nd argument of axios.post() (if using the two-argument signature) or the 3rd argument (if using the three-argument signature). This example demonstrates the two-argument signature of axios.post() that sets the headers with the dataresult of a previous request:
export default {
methods: {
async sendRequest() {
const userResp = await axios.get('https://reqres.in/api/users/2')
await axios.post('https://reqres.in/api/users', {
headers: userResp.data,
data: {
name: 'john doe',
job: 'leader',
}
})
},
}
}
demo
Side note: The Access-Control-Allow-Origin is a CORS header that can only be set by the server. It has no effect when sent from the client. It's possible you're incorrectly assuming that header is not reaching the server because it's not resolving a CORS issue.

vertx Upload-File correct approach

I created 2 servers here
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.post("/api/upload").handler(routingContext -> {
System.out.println(routingContext.fileUploads().size());
routingContext.response().end();
});
vertx.createHttpServer().requestHandler(req -> {
router.accept(req);
}).listen(8080, listenResult -> {
if (listenResult.failed()) {
System.out.println("Could not start HTTP server");
listenResult.cause().printStackTrace();
} else {
System.out.println("Server started");
}
});
// ==========================================
vertx.createHttpServer().requestHandler(req -> {
req.bodyHandler(buff -> {
System.out.println(buff.toString() + " from client");
req.response().end();
});
}).listen(8081, listenResult -> {
if (listenResult.failed()) {
System.out.println("Could not start HTTP server");
listenResult.cause().printStackTrace();
} else {
System.out.println("Server started");
}
});
The 1st one is from vertx documentation.
The 2nd one is from https://github.com/vert-x3/vertx-examples/blob/master/web-client-examples/src/main/java/io/vertx/example/webclient/send/stream/Server.java
When tested with Postman, both works.
When tested with other front-end codes, (example: https://github.com/BBB/dropzone-redux-form-example), only 2nd server works.
This is what I updated on the above github example.
fetch(`http://localhost:8081/api/upload`, {
method: 'POST',
headers: {
},
body: body,
})
.then(res => {
console.log('response status: ', res.statusText);
return res.json();
})
.then(res => console.log(res))
.catch(err => {
console.log("An error occurred");
console.error(err);
});
In practice, I prefer to use the approach to 1st server.
Since both are tested by Postman, I believe server is not an issue, and need to tweak on the client side.
Can anyone point out what I should be adding to the client?
Thanks.
Edit
axios.post('http://localhost:50123/api/upload', fileData)
.then(response => {
console.log('got response');
console.dir(response);
})
.catch(err => {
console.log("Error occurred");
console.dir(err);
});
axios works when passing a file from frontend.
Now the problem is unit-test using Vertx Web Client.
fs.open("content.txt", new OpenOptions(), fileRes -> {
if (fileRes.succeeded()) {
ReadStream<Buffer> fileStream = fileRes.result();
String fileLen = "1024";
// Send the file to the server using POST
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.putHeader("content-length", fileLen)
.sendStream(fileStream, ar -> {
if (ar.succeeded()) {
// Ok
}
});
}
});
The above code from http://vertx.io/docs/vertx-web-client/java/#_writing_request_bodies doesn't work for 1st server. FileUploads is empty.
It works for 2nd.
Edit2
I decided to use a simple HttpClient code, and it works as well.
How can I make a multipart/form-data POST request using Java?
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost uploadFile = new HttpPost("http://localhost:8080/upload");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("field1", "yes", ContentType.TEXT_PLAIN);
// This attaches the file to the POST:
File f = new File("./test.txt");
builder.addBinaryBody(
"file",
new FileInputStream(f),
ContentType.APPLICATION_OCTET_STREAM,
f.getName()
);
HttpEntity multipart = builder.build();
uploadFile.setEntity(multipart);
CloseableHttpResponse response = httpClient.execute(uploadFile);
HttpEntity responseEntity = response.getEntity();
System.out.println(responseEntity.toString());
I don't see how your last example could work. You post to http://localhost:8080/upload, but your route is /api/upload. In your second example, with port 8081 you simply ignore the route, and assume that anything you receive is a file upload. That's the only reason second example "works".

axios.post is returning error when used with redux-saga

I recently converted my redux-thunk middleware code to use redux-saga and it was working all these days fine and all of a sudden it is throwing an error. Not sure why!!
My Spring Boot REST Client is returning the proper response and no errors in the log. And if i make the same request using swagger i am getting the response back as expected so there is nothing wrong on the server side.
I have the following code
const LOGIN_URL = 'http://localhost:8888/api/a/login';
export function* loginUserAsync(action) {
console.log('.loginUserAsync() : action:', action);
yield put({ type: LoginConstants.LOGIN_USER_IN_PROGRESS });
const postParams = {
username: action.props.username,
password: action.props.password
};
const headerParams = {
headers: {
'Content-Type': 'application/json',
//'Content-Type': 'x-www-form-urlencoded'
}
};
console.log('headerParams', headerParams);
console.log('postParams', postParams);
try {
console.log('Before making async post call using axios');
const response = yield call(axios.post, LOGIN_URL, postParams, headerParams);
let token;
console.log('response', response);
if (response.headers) {
token = response.headers['x-auth-token'];
AsyncStorage.setItem('jwt', token);
}
// Login Succeeded fire Login Success Action
yield put({
type: LoginConstants.LOGIN_USER_SUCCESS,
token,
account: response.data
});
const navigatorUID = Store.getState().navigation.currentNavigatorUID;
Store.dispatch(NavigationActions.push(navigatorUID, Router.getRoute('home')));
} catch (error) {
// Login Failed fire Login Failure Action
console.log('loginUserAync() : error:[' + JSON.stringify(error) + ']');
yield put({
type: LoginConstants.LOGIN_USER_FAILURE,
error: error.data
});
}
}
export function* loginUser() {
console.log('.loginUser() :');
yield takeEvery(LoginConstants.LOGIN_USER, loginUserAsync);
}
In the console i am seeing the following:
I have no idea why it stopped working all of a sudden.
Thanks
Sateesh
For some reason localhost and 127.0.0.1 are not being recognized and i have to use the actual IP Address.
I had that Issue when i tried to run it in my mac book. It always worked with localhost in Ubuntu.

Sails JS with Redis for caching

As I said in my previous questions, I am trying to learn how to use sails.js, what I'm trying to do now is to cache the response of an api to redis. I have searched on how to do this, but I can't make it to work. Without caching, I call the api through ajax.
Any thoughts on how I will be able to do it using my controller? How can I call the api using the controller in sails.js and cache the response using redis?
You can use https://github.com/mranney/node_redis
Steps:
Add to package.json
"redis": "^0.12.1"
Run
npm install
Create a service module /api/services/CachedLookup.js
var redis = require("redis"),
client = redis.createClient();
module.exports = {
rcGet: function (key, cb) {
client.get(key, function (err, value) {
return cb(value);
});
},
fetchApi1: function (cb) {
var key = 'KEY'
CachedLookup.rcGet(key, function (cachedValue) {
if (cachedValue)
return cb(cachedValue)
else {//fetch the api and cache the result
var request = require('request');
request.post({
url: URL,
form: {}
}, function (error, response, body) {
if(error) {
//handle error
}
else {
client.set(key, response);
return cb(response)
}
});
}
});
}
}
Inside the controller
CachedLookup.fetchApi1(function (apiResponse) {
res.view({
apiResponse: apiResponse
});
});