Aurelia Post with http-fetch-client producing an options request - api

I'm creating a small forum where people in our company can put up adverts for goods or services they want to sell on the fly, using aurelia. I have a list of adverts page working fine, a details page for each advert working fine both using get requests from an api. However i can't seem to get the work the Post reqeust when someone wants to add a comment on an advert.
#inject(HttpClient)
export class ApiData {
constructor(httpClient) {
httpClient.configure(config => {
config
.withBaseUrl("MyUrl");
});
this.http = httpClient;
//.configure(x => {x.withHeader('Content-Type', 'application/json');});
}
postAdvertComment(comment, id) {
return this.http.fetch(`/adverts/${id}/comments`, {
method: "post",
body: json(comment),
headers: {
'Accept': 'application/json'
}
});
}
getAdverts() {
return this.http.fetch("/adverts")
.then(response => {
return this.adverts = response.json();
});
}
getAdvert(id) {
return this.http.fetch(`/adverts/${id}`)
.then(response => {
return this.advert = response.json();
});
}
}
Doing this project we've had some issue with CORS, all solved by adding in AllowCors tags in the api, including all methods etc.
<add key="CorsAllowedOrigins" value="*" />
<add key="CorsAllowedHeaders" value="" />
<add key="CorsAllowedMethods" value="*" />
However when i try and run the post, its running an options method and returns a 400 Bad request.
Here
We also get the following CORS error:
Fetch API cannot load MyURL/api/adverts/2/comments. Response to preflight
request doesn't pass access control check: No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin 'http://localhost:49877' is
therefore not allowed access. The response had HTTP status code 400. If an
opaque response serves your needs, set the request's mode to 'no-cors' to fetch
the resource with CORS disabled.
I don't know if it's a problem with our c# api or with how I'm trying to post from aurelia, but we have tried sending requests from postman and it works fine, tried sending post request within the same app using jquery and it works fine, and all the get requests work fine, but for some reason this post is causing all sorts of problems.

It seems to be a problem in your WebAPI, but before giving you some possible solutions I'd like to show you some important things.
Postman is not affected by CORS, so all requests work.
jQuery ajax uses XHR (XmlHttpRequest object) while aurelia-fetch-client uses fetch (window.fetch. However, the fetch-polyfill uses XHR in the background). They are
different approaches to solve the same problem. Just because one of them work, doesn't actually mean that the other one should work too.
The OPTIONS request is made by fetch, that's how it works. More information here https://developers.google.com/web/updates/2015/03/introduction-to-fetch?hl=en
To solve this problem try to remove those tags from web.config, and allow CORS in your Startup.cs. Like this:
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll); //or another parameter
//rest of your code
}
You don't have to set the content-type header to application/json. It is automatically made when you use the json() function ---> body: json(comment)
If you are using OWIN you might have to send the content-type as x-www-form-urlenconded. In that case, take a look at this Post 'x-www-form-urlencoded' content with aurelia-fetch-client

Related

receive JSON array data from fetch or axios.get VueJS

I am going mental. So I have the code below. When I run it as is, I get
Access to XMLHttpRequest at 'URL' from origin 'http://localhost:8000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If I remove the CORS header I get the same error. I am not sure what I am doing wrong. I will tell you if I curl the URL it works fine. What am I missing here.
Also, I have tried this using both axios.get AND fetch so I am open to both solutions.
getRoomUserCount: function () {
return axios
.get('URL', {
responseType: "json",
});
},
What it SHOULD return is an array of data, then using the code below I grab the array
mounted: function () {
this.roomUserCount = this.getRoomUserCount();
},
CORS policy is ALWAYS server related. You cannot change anything on your client-side to change that.
Enable CORS on your server to fix this.

Nuxt SSR - i can't check if a user is authenticated

I'm trying to work on a Nuxt SSR frontend that uses a Django backend with Session Authentication.
I would like to have some SSR pages as well as client rendered pages in my frontend, so i'm using Universal mode.
The problem is that i did not find a working approach to check if a user is authenticated before loading a page, so i can't restrict pages to anonymous users. In order to check if a user is authenticated, Django will check if the request's headers contain a cookie, and according to that return if the user is authenticated or not.
Here is what i tried:
1) Middleware
export default async function ({context, redirect}) {
axios.defaults.withCredentials = true;
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//Redirect if user is authenticated
}).catch(function (error) {
console.log(error)
});
}
Here i'm sending a request to my backend to check if the user is authenticated. The problem is that the middleware is executed from server side, which means there will never be any cookie in the request, even if the user is authenticated. This means that every time i refresh the page, according to the middleware the user is always anonymous, even when the user is authenticated.
2) Plugin
export default function (context, inject) {
if (process.client){
console.log('client')
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//IF AUTHENTICATED, REDIRECT
context.redirect('/')
}).catch(function (error) {
console.log(error)
});
} else {
console.log('server')
}
}
Here i'm trying the same but with a plugin, and i'm "forcing" the plugin to check if the user is authenticated on the backend only when the plugin executes from client side. This works, cookies are sent in the headers and Django receives the cookie, but the problem with this solution is that Nuxt doesn't allow redirecting to other pages from a plugin (https://github.com/nuxt/nuxt.js/issues/4491).
3) Using beforeMount() in Vue
I tried to do that using beforeMount() from my Vue pages, but the problem is that since it will execute AFTER idration, the page will be loaded and after 1/2 seconds the redirect happens, which is kind of ugly.
Is it possible that there isn't a way to do this? Any kind of advice is appreciated
EDIT: the problem is not that i don't know how to code this, the problem is that when Nuxt sends a request to my backend from the server side middleware, the request will not contain any cookie, and because of this my Django backend cannot check the session cookie, which means that the backend cannot check whether or not the user is authenticated. The same code works when the middleware is executed from client side (if i navigate directly to the page instead of refreshing), because the request will contain the cookies.
I'm trying to understand if this is normal or not, but this could be an issue with Nuxt.
I know this a year old question and it was probably about nuxt 2, now nuxt 3 is out and running and I found my self with the same problem and here is how I solved it, just in case someone stumble here just like I did.
With Nuxt 3 server side you can use useFetch with the options headers: useRequestHeaders(['cookie'])
const { data, error, pending, refresh } = await useFetch(api.auth,
{
credentials: "include",
headers: useRequestHeaders(['cookie'])
}
);
There are a few issues you need to be aware of:
_ The cache, if you perform the same request with the same parameters it will return the same cached response (it won't even call the end point API). Try to use the key option with different values or the returned refresh method and check the doc "Data fetching" for more info.
_ The cookies, any cookie generate server side won't be shared with the client side, this means if the API generate a new token or session cookie on server side the browser won't receive those cookies and may generate new ones, this may get you in some 400 - bad request if you use session with CSRF, check this issue for more info.
I do have a working middleware with this
export default ({ redirect, store }) => {
if (store?.$auth?.$state?.loggedIn) {
redirect('https://secure.url')
} else {
redirect('https://login.please')
}
})

Set-Cookie not accessible through axios or fetch

I am building a web application with a go backend and a vue.js frontend.
I want to do a simple sign in form in which I send the sign in request from a method of my component with Axios (or fetch) and get in response a JSON object of the user and a session token in the cookie to be stored and reused in future requests to the server.
The code of my components method :
class LoginComponent extends Vue {
sendLogin (): void {
axios.post<User>('http://192.168.1.227:8080/signin', body)
.then(res => console.log('Axios Response :', res)
.catch(err => console.error('Axios Error :', err))
}
}
The part of the code of the go server :
go API
with the headers :
go headers
the front and backend are on different IP addresses in a local network and they communicate through HTTP.
The problem that I faced is that when receiving the response after the post request to login I don't have access to the cookie that has been set by the server. When I use Axios to analyze the response the cookie isn't in the headers whereas when I look at the network logs in the browser, the cookie is in the headers but it is not saved and it is not sent when I do another request.
Also, the only header that is visible with Axios is Content-Type : application/json; charset=UTF-8
I tried many things to be able to see this cookie but it doesn't work :
adding { withCredentials: true } to the axios request or axios.defaults.withCredentials = true to the axios instance only stops the request because of CORS.
changing all the Access-Control headers to "*" didn't change anything
using { auth: { username: 'foo', password: 'bar' } } in the axios options instead of the body
The only thing that worked and automatically saved the cookie was to send the request via the attributes of the form html tag, like so :
<form method="POST" action="http://192.168.1.227/signin">
...
</form>
But this way I am redirected to the JSON response object and not to one of my routes from vue-router and I can't access the User object in my app.
Is there any way that my problem can be solved?
Ok so the comment of Зелёный was the answer.
I needed the go server to set Access-Control-Allow-Origin: http://192.168.1.218:8080 (the address of the frontend) and then configure axios with { withCredentials: true } to be able to automatically store the cookie. Although I still don't see it when I do a console.log on the axios response, it is successfully stored and reused for each call to the server.

Sending axios get request with authorization header

I have tried to send axios get request using vue.js and it worked just fine when there was no need to send headers. However, when it was required to send an authorization jwt, i was getting CORS error: "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource." I don't know why is this problem occurring since there is Access-Control-Allow-Origin: '*' header in the response. My code is the following:
axios.get(url, {
headers: {
'Authorization': 'Bearer TOKEN'
}
})
.then(function (response) {
console.log(response.data)
})
The weirdest thing is when I use querystring.stringify or JSON.stringify on the header, I don't get the error 403(forbidden), but just an error 401 - Unauthorized. I tried with variable and with the token itself and it didn't work.
I tried to send a post request in order to get a web token with required data - username an password and it worked. I was able to get the token.
I made a whole bunch of research the last two days on this and I found different kind of request structure and configs which I tried all of them, but none were efficient. Is there a way to check if the request is being send with the header? Is something else the problem? If someone can help, I would appreciate. Thanks.
I think you should add this code to the bootstrap.js (or where the axios is defined):
window.axios = require('axios'); // I think its already added
window.axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest'
};
You didn't mention, but I guess you use laravel, or other framework, what is protected from csrf attack, thats why you need to add the generated token to your ajax request header.

Vue.js click event firing twice with vue-resource [duplicate]

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script>
$.get("http://example.com/", function(data) {
alert(data);
});
</script>
it does an OPTIONS request to that URL, and then the callback is never called with anything.
When it isn't cross domain, it works fine.
Shouldn't jQuery just make the call with a <script> node and then do the callback when its loaded? I understand that I won't be able to get the result (since it is cross domain), but that's OK; I just want the call to go through. Is this a bug, or am I doing something wrong?
According to MDN,
Preflighted requests
Unlike simple requests (discussed above), "preflighted" requests first
send an HTTP OPTIONS request header to the resource on the other
domain, in order to determine whether the actual request is safe to
send. Cross-site requests are preflighted like this since they may
have implications to user data. In particular, a request is
preflighted if:
It uses methods other than GET or POST. Also, if POST is used to send
request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or text/plain,
e.g. if the POST request sends an XML payload to the server using
application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as
X-PINGOTHER)
The OPTIONS is from http://www.w3.org/TR/cors/ See http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/ for a bit more info
If you're trying to POST
Make sure to JSON.stringify your form data and send as text/plain.
<form id="my-form" onSubmit="return postMyFormData();">
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email" required>
<input type="submit" value="Submit My Form">
</form>
function postMyFormData() {
var formData = $('#my-form').serializeArray();
formData = formData.reduce(function(obj, item) {
obj[item.name] = item.value;
return obj;
}, {});
formData = JSON.stringify(formData);
$.ajax({
type: "POST",
url: "https://website.com/path",
data: formData,
success: function() { ... },
dataType: "text",
contentType : "text/plain"
});
}
Just change the "application/json" to "text/plain" and do not forget the JSON.stringify(request):
var request = {Company: sapws.dbName, UserName: username, Password: userpass};
console.log(request);
$.ajax({
type: "POST",
url: this.wsUrl + "/Login",
contentType: "text/plain",
data: JSON.stringify(request),
crossDomain: true,
});
I don't believe jQuery will just naturally do a JSONP request when given a URL like that. It will, however, do a JSONP request when you tell it what argument to use for a callback:
$.get("http://metaward.com/import/http://metaward.com/u/ptarjan?jsoncallback=?", function(data) {
alert(data);
});
It's entirely up to the receiving script to make use of that argument (which doesn't have to be called "jsoncallback"), so in this case the function will never be called. But, since you stated you just want the script at metaward.com to execute, that would make it.
In fact, cross-domain AJAX (XMLHttp) requests are not allowed because of security reasons (think about fetching a "restricted" webpage from the client-side and sending it back to the server – this would be a security issue).
The only workaround are callbacks. This is: creating a new script object and pointing the src to the end-side JavaScript, which is a callback with JSON values (myFunction({data}), myFunction is a function which does something with the data (for example, storing it in a variable).
I had the same problem. My fix was to add headers to my PHP script which are present only when in dev environment.
This allows cross-domain requests:
header("Access-Control-Allow-Origin: *");
This tells the preflight request that it is OK for the client to send any headers it wants:
header("Access-Control-Allow-Headers: *");
This way there is no need to modify the request.
If you have sensitive data in your dev database that might potentially be leaked, then you might think twice about this.
In my case, the issue was unrelated to CORS since I was issuing a jQuery POST to the same web server. The data was JSON but I had omitted the dataType: 'json' parameter.
I did not have (nor did I add) a contentType parameter as shown in David Lopes' answer above.
I was able to fix it with the help of following headers
Access-Control-Allow-Origin
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Allow-Methods
If you are on Nodejs, here is the code you can copy/paste.
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin','*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH');
next();
});
It's looking like Firefox and Opera (tested on mac as well) don't like the cross domainness of this (but Safari is fine with it).
You might have to call a local server side code to curl the remote page.