POST request treated as OPTIONS on beego framework - javascript

I'm using beego framework as my API framework and AngularJS on the client.
I have set all CORS setting correctly. I can do GET request. But, when i try to POST, beego treat is as OPTIONS request. It also throw a warning: multiple response.WriteHeader calls. what could possibly wrong?
my beego CORS setting:
func init() {
orm.RegisterDataBase("default", "mysql", "root:#tcp(127.0.0.1:3306)/fakeapi")
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowOrigins: []string{"*"},
AllowMethods: []string{"GET", "DELETE", "PUT", "PATCH", "POST"},
AllowHeaders: []string{"Origin"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
}))
}
My ANgularJS request
var transaction = $http.post(BASE_URL + "transaction", transactionData);
return $q.all([transaction]).then(function(response) {
console.log(response);
});
my system:
Ubuntu 14.04
beego: 1.4.2
bee: 1.2.4
angularJS: 1.3.12

That might because of an issue/pull request currently pending to be merged into master: issue 912
Without this line everything is fine:: router.go#L861
That seems to be in line with commit 3bb4d6f which shows:
// Write status code if it has been set manually
// Set it to 0 afterwards to prevent "multiple response.WriteHeader calls"
(and router.go do set a status, hence the error message)
Commit f962457 is supposed to solve this issue, but isn't merged yet.
The other issue 904 mentions something about being unable to retrieve the Session data previously registered in the Session Engine.
Maybe Session.on flag can help.

I handle it like that, I hope it helps
import (
_ "info_apoyo/routers"
"github.com/astaxie/beego"
"github.com/astaxie/beego/plugins/cors"
)
func main() {
if beego.BConfig.RunMode == "dev" {
beego.BConfig.WebConfig.DirectoryIndex = true
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
}
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowOrigins: []string{"*"},
AllowMethods: []string{"GET", "POST", "DELETE", "PUT", "PATCH"},
AllowHeaders: []string{"Origin", "content-type", "Access-Control-
Allow-Origin"},
ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-
Origin"},
AllowCredentials: true,
}))
beego.Run()
}

Related

Axios get call in Vue3 not working, although curl and javascript work as expected

I'm trying to make an API call from my Vue3 app. The prepared API has an endpoint like http://localhost:8888/api/dtconfigsearch, where one needs to pass a json payload like { "Modelname": "MyFancyModel"} to get the full dataset with the given modelname. Pure get functions without a payload / a body do work from my Vue3 project to the golang backend, but I'm having problems with passing a payload to the backend.
Test with curl -> ok
$ curl -XGET localhost:8888/api/dtconfigsearch -d '{"Modelname" : "MyFancyModel" }'
{"ID":4,"Modelname":"MyFancyModel","ModelId":"96ee6e80-8d4a-b59a-3524-ced3187ce7144000","OutputTopic":"json/fancyoutput"}
$
This is the expected output.
Test with javascript ok
Source file index.js:
const axios = require('axios');
function makeGetRequest() {
axios.get(
'http://localhost:8888/api/dtconfigsearch',
{
data: { Modelname : "MyFancyModel" },
headers: {
'Content-type' : 'application/json'
}
}
)
.then(resp => {
console.log(resp.data)
})
.catch(err => {
console.log(err)
})
}
makeGetRequest()
Output
$ node index.js
{
ID: 4,
Modelname: 'MyFancyModel',
ModelId: '96ee6e80-8d4a-b59a-3524-ced3187ce7144000',
OutputTopic: 'json/fancyoutput'
}
$
Here, I also get the desired output.
Test within Vue fails :-(
Source in the Vue one file component:
onSelection(event) {
let searchPattern = { Modelname : event.target.value }
console.log(event.target.value)
console.log("searchPattern = " + searchPattern)
axios.get("http://localhost:8888/api/dtconfigsearch",
{
data : { Modelname : "Windshield"},
headers: {
'Content-type' : 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err)
alert("Model with name " + event.target.value + " not found in database")
})
},
Output in browser:
In the image you can see in the terminal log on the right side that the backend is not receiving the body of the API call. However, in the browser information of the call there is content in the config.data part of the object tree, which is the payload / the body. The only thing that bothers me that it is not a json object, but stringified json, although it was entered as json object. According to the documentation, the parameter name (data) in the call should be correct to hold the body content of the api call.
I've tried different header information, looked if it could be a CORS issue, what it isn't to my opinion, exchanged key data with body, used axios instead of axios.get and adapted parameter, all without success. The version of the axios library is 0.27, identical for Vue and vanilla javascript. After checking successfully in javascript, I was sure that it would work the same way in Vue, but it didn't.
Now I'm lost and have no further ideas how to make it work. Maybe someone of you had similar issues and could give me a hint? I'd be very grateful for some tipps!!

How to modify request headers using Puppeteer & Chrome DevTools Protocol? (Possibly a JS syntax issue)

I have the following Typescript function that assumes a Chrome browser has already been launched using Puppeteer. The documentation for the Fetch functions used below can be found here.
async function modify(client: CDPSession) {
client.on('Fetch.requestPaused', async ({ requestId, request, frameId, resourceType, responseErrorReason, responseStatusCode, responseHeaders, networkId }) => {
// Correctly prints out the User-Agent header's value
console.log(request.headers["User-Agent"]);
// After this line is run, I can inspect the request.headers object and see that User-Agent was successfully edited
request.headers['User-Agent'] = 'trying to edit this header';
// Continuing the request gives an error
await client.send('Fetch.continueRequest', {
requestId: requestId,
headers: request.headers,
});
});
}
Here is the specific error I'm seeing:
Error: Protocol error (Fetch.continueRequest): Invalid parameters headers: array expected
How can I resolve this error and successfully modify the request.headers? Is this a silly Javascript/Typescript syntax issue that I just can't figure out?
Fetch.requestPaused returns the headers as an object. e.g.:
{
"Upgrade-Insecure-Requests":"1",
"Accept": "text/html,application/xhtml+xml"}
}
Fetch.continueRequest expects an Array<{name: string, value: string}>. e.g.
[
{"name": "Accept", value: "text/html,application/xhtml+xml"}
]
You can use the code that Puppeteer is using:
function headersArray(headers) {
const result = [];
for (const name in headers) {
if (!Object.is(headers[name], undefined))
result.push({name, value: headers[name] + ''});
}
return result;
}

Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body in react-admin

I am using react-adminframework, and I have written my own DataProvider. I am trying to accomplish that when an User is created, an instance of UserPossession is created as well. My code bellow accomplishes that, but react-admin Front-end just displays the warning message:
Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body
I checked the Network tab in Developer Tools and every request to server is correct, there is no error. Which leaves me confused and stuck with this, because I have no idea what that warning means or why is it even occuring.
My code is a part of convertDataRequestToHTTP constant and looks like this:
if (resource === 'User') {
url = `${apiUrl}/${resource}`;
options.body = params.data;
httpClient(url, {
method: 'POST',
body: JSON.stringify(options.body),
})
.then(response => (
url = `${apiUrl}/Location`,
options.method = 'POST',
options.body = JSON.stringify({
"odata.type": "HardwareDatabase.UserPossession",
"Name": response.json.Login,
"UserId": response.json.Id
}),
httpClient(url, {
method: options.method,
body: options.body
})
));
}
If you have any questions regarding the code I can clarify.
Thank you for any ideas in advance.
Since you are stating that this code snippet is a part of convertDataRequestToHTTP I might see the issue. httpClient cannot be used in this constant since it creates duplicit calls to API or in your case, this Warning. Correct way would be to only state the options constant.
url = `${apiUrl}/${resource}`;
options.body = JSON.stringifiy(params.data);
options.method = 'POST';
Later in the constant that converts response from OData to mandatory React Admin format, state the httpClient.
params.data = {
"odata.type": "HardwareDatabase.UserPossession",
"Name": response.json.Login,
"UserId": response.json.Id
};
httpClient(`${apiUrl}/Location`, {
method: 'POST',
body: JSON.stringify(params.data),
})
Unfortunately, the GET method for XMLHttpRequest and fetch don't support request bodies.
A temporary work around I found was to use the axios library, which does let you send GET with request bodies.
const res = await axios.get("/api/devices", {
data: { deviceName: 'name' }
})

angular resource's cache doesn't work at all

I'm trying to add caching to a resource, but at each call, a new ajax request would be made. Here:
app.factory('States', function($resource,$locale,$cacheFactory) {
var cache = $cacheFactory('statesCache');
var StatesService = $resource( "api/ref/states-:country", {}, {
query: {
cache: cache, //With true it doesn't work either
method: 'GET',
isArray: true
}
});
return {
getList: function( country ) {
console.log( cache.info() );
return StatesService.query({
country: (country || "CA").toLowerCase()
});
}
}
});
then each call to States.getList( "CA" ); triggers a new Http request. The console says each time that the custome cache size is still 0: Object {id: "statesCache", size: 0}
Angular version is v1.2.0-rc.3 was 1.0.8
The problem was Chrome didn't invalidated the cache for the angular files. It was still loading angular 1.0.8.
I've cleaned the cache and decently updated angular-resource version, now it works.

Access-Control-Allow-Origin using ShareJS

I have my website on one domain/server: www.mysite.com and I'm running ShareJS on another server: www.my-other-server.com:8000.
www.mysite.com/index.html
<script src="http://www.my-other-server.com:8000/bcsocket.js"></script>
<script src="http://www.my-other-server.com:8000/share.js"></script>
<script src="http://www.my-other-server.com:8000/textarea.js"></script>
...
<textarea id='sharetext' ></textarea>
<script>
// get the textarea element
var elem = document.getElementById("sharetext");
// connect to the server
var options = {
origin: "www.my-other-server.com:8000",
browserChannel:{cors:"*"}
}
var connection = sharejs.open('test', 'text', options, function(error, doc) {
doc.attach_textarea(elem);
});
</script>
I get the following error in the JS console:
XMLHttpRequest cannot load http://www.my-other-server.com:8000/test?VER=8&MODE=init&zx=v44znr3caqea&t=1. Origin http://www.mysite.com is not allowed by Access-Control-Allow-Origin.
This ShareJS GitHub Issue (https://github.com/share/ShareJS/issues/77) suggests adding browserChannel:{cors:"*"} to the share options, as I did above, but it did not seem to have any effect...
What do I do here? It's important that my sharejs traffic is on a separate server than my static/dynamic web server.
On server side in node.js, if you are using express.js you need to add extra headers, that will allow cross-domain traffic from server side:
app.configure(function() {
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
next();
});
app.set('jsonp callback', true);
});
On client side you still might end up with security issues, so it is even better to use JSONP, so from server side response like that:
res.jsonp({ hello: 'world' });
And on client side AJAX like that:
$.ajax({
url: "www.my-other-server.com:8000",
type: 'GET',
dataType: 'jsonp',
success: function(data) {
console.log(data)
},
error: function(xhr, status, error) {
console.log('error[' + status + '] jsonp');
}
});
Try adding browserChannel: { cors:"*" } in bin/options.js. It should work.
The final options.js may look like this
// ShareJS options
module.exports = {
// Port to listen on
port: 8000,
// Database options
db: {
// DB type. Options are 'redis', 'couchdb' or 'none'. 'redis' requires the
// redis npm package.
//
// If you don't want a database, you can also say db: null. With no database,
// all documents are deleted when the server restarts.
// By default, sharejs tries to use the redis DB backend.
type: 'redis',
// The prefix for database entries
prefix: 'ShareJS:',
// The hostname, port and options to pass to redis.
// null lets the database decide - redis by default connects to localhost port 6379.
//hostname: null,
//port: null,
//redisOptions: null
// To use CouchDB uncomment this section then run bin/setup_couch.
// Database URI Defaults to http://localhost:5984/sharejs .
//type: 'couchdb',
//uri: "http://admin:admin#localhost:5984/ot",
// To use postgresql uncomment this section then run bin/setup_pg
//type: 'pg',
//uri: 'tcp://josephg:#localhost/postgres',
// By default, sharejs will create its tables in a schema called 'sharejs'.
//schema: 'sharejs',
//operations_table: 'ops',
//snapshot_table: 'snapshots',
// sharejs will automatically try and create the DB tables if they don't exist. You
// can create the database tables manually using bin/setup_pg.
//create_tables_automatically: true,
},
// The server will statically host webclient/ directory at /share/*.
// (Eg, the web client can be found at /share/share.js).
// Set staticpath: null to disable.
staticpath: '/share',
// REST frontend options. Set rest: null to disable REST frontend.
rest: {
},
// SocketIO frontend options. Set socketio: null to disable socketIO frontend.
socketio: {
// Specify tuples for io.configure:
// 'transports': ['xhr-polling', 'flashsocket']
},
// Browserchannel server options. Set browserChannel:null to disable browserchannel.
browserChannel: {cors:"*"},
// Authentication code to test if clients are allowed to perform different actions.
// See documentation for details.
//auth: function(client, action) {
// action.allow();
//}
}

Categories

Resources