I used swagger on express js and when I do a query I am getting this warnings.
WARNING! Unable to find a Swagger operation that matches HEAD ... - this will show if I will try to use curl.
WARNING! Unable to find a Swagger operation that matches OPTIONS ... - while this one is when accessed from a webpage.
I already add helmet and this to the code.
app.use((_, res: Response, next: NextFunction) => {
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
res.header(
'Access-Control-Allow-Headers',
'Content-Type, api_key, Authorization',
);
next();
});
What I miss?
Finally got it working, I mean the warning is not showing anymore. The solution is just a one liner, sending HTTP status of 200 telling the browser that the request is supported.
export function optionCORS(req: Request, res: Response, next: NextFunction): void {
// I am just setting the headers if the request is an OPTION or HEAD
// For now I didn't interfere with the other request like POST, etc.
// I have a guess that swagger is is doing it.
if (req.method === 'OPTIONS' || req.method === 'HEAD') {
const origin: string = req.headers.origin as string;
// On my angular I have interceptor that will set the ```withCredentials```
// option to true, so I cant use * on Allow-Origin so I grab the
// request.headers.origin an used it.
// Also we need to set the Allow-Credentials to true.
res.header('Access-Control-Allow-Origin', origin);
res.header('Access-Control-Allow-Credentials', 'true');
// Then the usual headers.
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS');
res.header(
'Access-Control-Allow-Headers',
'Accept, Authorization, Content-Type, Content-Length, Origin, ' +
'X-Requested-With',
);
// And this what I miss, just the HTTP 200 status.
// So the client will know that the request is supported.
res.sendStatus(200);
return;
}
// If the request is not an OPTIONS or HEAD continue as usual,
// it look like swagger is handling it.
next();
}
Then you can use it as middleware.
app.use(optionCORS);
If you using swagger-express-middleware set WARN=off environment variable
https://github.com/APIDevTools/swagger-express-middleware/blob/ed73f82e57adb868b10e1989dac555b8d943dab8/lib/helpers/util.js#L27
Related
I`m trying to realize redirecting to another page of my app (current location 'http://localhost:3000/create-item' redirect to 'http://localhost:3000/subscribe') after saving item to database on my express server using next code (client send POST message to endpoint with form data):
await item.save();
res.writeHead(302, { Location: 'http://localhost:3000/subscribe' });
res.end();
one more variant:
await item.save();
res.redirect('/subscribe');
In base file of my server index.js i use my custom CORS middleware:
//cors.middleware.js
function cors(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, PUT, PATCH, POST, DELETE, OPTIONS');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept, Authorization',
);
next();
}
module.exports = cors;
//index.js
const corsMiddleware = require('./middleware/cors.middleware');
app.use(corsMiddleware);
I also tryed:
npm i cors
app.use(cors());
Nevertheless I recieve CORS error while redirecting. So, what I`m doing wrong? Maybe this is related to sending data with POST method (redirect inside GET endpoint working well without CORS erro)
I have a working servlet that tests properly with Postman, but I can't get the request to execute from the front end. The fact that Postman can execute the servlet with either a Get or a Post tells me the problem is likely with the front-end code.
Does anyone see where the misconfiguration is in this block? The Basic key and cookie are copied from Postman, there is no CORs problem.
const response = await axios.get(url, null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Accept': '*/*',
'Content-type': 'application/json',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Auth-Token',
'Authorization': 'Basic YWRtaW46YWRtaW4='
},
withCredentials: true,
Cookie: "cq-authoring-mode=TOUCH;",
params: {
path: rootPath,
maxCount: sourceMax
}
}).catch(err => {
console.log(err)
}, () => {
console.log(response)
}).then(res => {
console.log(res)
})
This is most likely the CSRF filter which rejects some requests that don’t contain a CSRF token. By default it checks only POST, PUT and DELETE requests.
It’s weird that it also checks your request, which seems to be a GET. Either your filter is configured differently or you sending a Content-type header – which describes the request body content type – makes axios switch the request from GET to POST (because GETs don’t have a request body and, thus, don’t need to declare their content type).
The CSRF filter can be configured in various ways and can exclude certain requests from filtering by path or user-agent:
You could also request a token from the /libs/granite/csrf/token.json endpoint and then send it along in your request. One way to do this is via the query, as the :cq_csrf_token param.
The axios.post (code below) must send data to url api/add-todo, but I get these errors:
axios.post('http://localhost/vueoctober/todo/api/add-todo', todo).then(function (response) {
console.log(response);
}).catch(function(error) {
console.log(error);
});
The route api/add-todo is handled with October method Route::get() (https://octobercms.com/docs/services/router). Why is it not found?
If I change axios.post to axios.get it will be working! But I need post data, not get.
What I tried:
1) I tried to add these headers to .htaccess:
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTION"
It's working only for axios.get. The axios.post is still blocking.
2) I added Header set Access-Control-Allow-Origin "*" to httpd.conf.
Vue app is serving at port 8080, therefore axios.post url can't be relative.
I also stumbled and struggled with this on FF, even though I have this in the .htaccess:
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS".
After more searching I found a Gist by #marcomessa that fixed my issues.
https://gist.github.com/marcomessa/54d077a0208439f5340126ff629a3718
Look at the error message carefully, it says the response to the preflight request didn't have an HTTP ok status.
Clearly, your server-side code doesn't have a route handler for the OPTIONS request, so you need to add one.
As an aside, after the browser gets a successful OPTIONS response, it will make the POST request but you said:
The route api/add-todo is handled with October method Route::get()
You'll need to use Route::post() to handle that.
Hours of googling and I got answer...
1) Install plugin Cross-Origin Resource Sharing (CORS).
2) In htaccess of Vue app add:
Header set Access-Control-Allow-Origin '*'
Header set Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTION"
NOTICE! Write SET not ADD!
That's it.
So for clarification on this. There are always numerous ways to answer a problem. Here is what I did for mine. Check this for Preflight Request. The preflight request is created by the browser and is not for security. This function I created first will throw an okay message upon a request, then if the data contains any data it will then do what it is called (this is where you check for security). I don't have to mess with .htaccess files. Though I did install the CORS plugin because it is a nice plugin. Also the video from watch-learn does the author is making a cross-origin request in which he goes over how to correct the problem. I think he just filmed the video before preflight requests started to be a browser norm. Found routing information here.
Route::match(['POST', 'OPTIONS'],'api/update-todo', function(Request $req) {
$data = $req->input();
if (!empty($data)) {
Todo::where('id', $data['id'])
->update([
'name' => $data['name'],
'description' => $data['description'],
'status' => $data['status']
]);
return response()->json([
'Success' => $data,
]);
} else {
return response()->json([
'Success' => $req,
]);
}
});
I can not resolve it via axios, I wasted a lot of hours, but I resolved it very easy by this way.
Let's think we are posting:
{name:"Cynthia Merk", age:"22"}
I did the next function to send the last JSON (any JSON structure works):
const PostFunction = (data, letFunction, errorHandle) => {
let uri = "http://.../create.php";
let xhr = new XMLHttpRequest();
xhr.overrideMimeType("application/json");
xhr.open("POST", uri, true);
xhr.addEventListener("readystatechange", function() {
if(xhr.readyState === 4 && xhr.status === 200) {
letFunction(this.responseText);
}else{
errorHandle(this.responseText);
}
});
xhr.send(JSON.stringify(data));
}
You can invoke this function, it needs changes the "uri" variable value and it's required to use JSON.stringify to send the data.
In PHP the API is very easy too, for dummies:
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token, X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE");
header("Allow: POST, GET, OPTIONS, PUT, DELETE");
class DB extends PDO {
private $host = 'localhost';
private $dbname = 'zamuSysScheme';
private $user = 'root';
private $password = 'admin';
private $charset = 'utf8';
public function __construct(){
try{
$dns = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
parent::__construct($dns, $this->user, $this->password, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
} catch(PDOException $e){
echo 'Error: ' . $e->getMessage();
exit;
}
}
}
$method = $_SERVER['REQUEST_METHOD'];
$pdo = new DB();
if($_SERVER['REQUEST_METHOD'] == 'POST'){
$json = file_get_contents('php://input');
$decoded = json_decode($json, true);
try{
$Name = $decoded["name"];
$Age = $decoded["age"];
if(!isset($Name)){
header("HTTP/1.1 402 FAIL");
echo "The paramenter Name is not present";
exit;
}
if(!isset($Age)){
header("HTTP/1.1 403 FAIL");
echo "The paramenter Age is not present";
exit;
}
$sqlStatement = "INSERT INTO Client (Name, Age) VALUES";
$sqlStatement .= "(:Name, :Age)";
$stmt = $pdo->prepare($sqlStatement);
$stmt->bindValue(':Name', $Name, PDO::PARAM_INT);
$stmt->bindValue(':Age', $Age, PDO::PARAM_INT);
$stmt->execute();
$Client_Id = $pdo->lastInsertId();
if($Client_Id){
header("HTTP/1.1 200 OK");
echo $Pago_Id;
exit;
}
}catch(Exception $except){
header("HTTP/1.1 400 FAIL");
echo "Error: " . $json . " /// " . $except;
exit;
}
}
header("HTTP/1.1 401 BAD REQUEST");
?>
I hope it can help you, any question is allowed and if I have the answer I'll glad to help.
I have a VueJS application and a ExpressJS server side application. On my server I have the following:
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
next();
}
app.use(allowCrossDomain);
However, I keep getting the following error:
Request header field Authorization is not allowed by
Access-Control-Allow-Headers in preflight response.
I'm sending my request like this:
import axios from 'axios'
export default axios.create({
baseURL: 'https://example.com',
timeout: 5000,
headers: {
'Authorization': 'Bearer ' + accessToken
}
})
How can I get around this? I have tried many solutions and read up on this. I have read the below:
How to allow CORS?
CORS error :Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response
Request header field Access-Control-Allow-Headers is not allowed by itself in preflight response
The code in the question apparently isn’t causing the Access-Control-Allow-Headers header to get sent for OPTIONS responses — specifically, for the response to the CORS preflight OPTIONS.
To ensure preflight OPTIONS get handled correctly, consider installing the npm cors package:
npm install cors
And then do something like this:
var express = require('express')
, cors = require('cors')
, app = express();
app.options('*', cors()); // preflight OPTIONS; put before other routes
That will handle CORS preflight OPTIONS requests and other CORS aspects without you needing to manually write your own handling from scratch in your application code.
https://www.npmjs.com/package/cors#configuration-option has more details on all the options.
Normally, when I make a jQuery request to a non-local server, it applies Cross-site HTTP request rules and initially sends an OPTIONS request to verify the existence of an endpoint and then it sends the request, i.e.
GET to domain.tld/api/get/user/data/user_id
jQuery works fine, however I would like to use Vue Resource to deal with requests. In my network log, I see only the actual request being made (no OPTIONS request initially), and no data is being received.
Anybody has an idea how to solve this?
Sample Code:
var options = {
headers: {
'Authorization': 'Bearer xxx'
}
};
this.$http.get(config.api.base_url + 'open/cities',[options])
.then(function(response){
console.log('new request');
vm.cities = response;
}, function(error){
console.log('error in .js:');
console.log(error);
});
jquery-request
Solution:
As #Anton mentioned, it's not necessary to have both requests (environment negligible). Not sure what I have changed to make it work, but the request gave me an error. It consisted in setting the headers correctly. Headers should not be passed as options but as a property of http:
this.$http({
root: config.api.base_url + 'open/cities', // url, endpoint
method: 'GET',
headers: {
'Authorization': 'Bearer xxx'
}
}).then(function(response){
console.log('new request');
vm.cities = response;
}, function(error){
console.log('error in .js:');
console.log(error);
});
Thank you guys, it was a team effort :)
Is it a requirement that an additional OPTIONS request is being made? I have created a small (32 LOC) example which works fine and retrieves the data:
https://jsfiddle.net/ct372m7x/2/
As you can see, the data is being loaded from a non-local server. The example is located on jsfiddle.net and the request is made to httpbin.org - this leads to CORS being applied (you can see the Access-Control-Allow-Origin header in the screenshot below).
What you also see is that only the GET request has been executed, no OPTIONS before that.