12

Update

Checking my network tab in my dev-tools of firefox gives me this:

Response headers (290 B)    
Raw headers
Access-Control-Allow-Origin 
*
Allow   
POST
Cache-Control   
no-cache, private
Connection  
close
Content-Type    
text/html; charset=UTF-8
Date    
Sat, 31 Aug 2019 09:45:04 GMT
Date    
Sat, 31 Aug 2019 09:45:04 GMT
Host    
localhost:8000
X-Powered-By    
PHP/7.2.19-0ubuntu0.18.04.2
Request headers (438 B) 
Raw headers
* Accept: */* *
Accept-Encoding 
gzip, deflate
Accept-Language 
en-US,en;q=0.5
Access-Control-Request-Headers  
x-csrf-token,x-requested-with,x-xsrf-token
Access-Control-Request-Method   
POST
Connection  
keep-alive
Host    
localhost:8000
Origin  
http://127.0.0.1:8000
Referer 
http://127.0.0.1:8000/register
User-Agent  
Mozilla/5.0 (X11; Ubuntu; Linu…) Gecko/20100101 Firefox/68.0

Why is the method GET? Trying to change it from the tools, it gives that POST method isn't allowed. Also, when submitting the request, it gives the following info:

Update

I started making changes to bootstrap.js file, where my axios is required, but I had no success. I tried changing

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

to

window.axios = require('axios');

window.axios.defaults.headers.common = {
    'X-Requested-With': 'XMLHttpRequest',
    'X-CSRF-TOKEN' : document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
};

but really, I can't get to a point. I cannot understand how the linked tutorial in my comments worked and I am starting to have a shortage of ideas on what to look. So, any help is appreciated; if someone can point to a valid reference that says, look, this is an unsolved problem, then I will implement py project with a different code; but if not, I this problem shouldn't remain unsolved. If it's a minor mistake from my part, then where exactly is it? Thanks to all.

Note: On my original posts, I didn't showed what my web routes look like. Since I am using React from inside Laravel(I used in terminal php artisan preset react ;) my web routes went from the pre-configured laravel code

Route::get('/', function () {
    return view('welcome');
});

to the new code that calls the React app:

Route::view('/{path?}', 'app');

Update3: I have tried(from yesterday) to change my apache2 conf and my problem remains. I don't know if this is to be taken as change though:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8000/api/user/register. (Reason: missing token ‘x-requested-with’ in CORS header ‘Access-Control-Allow-Headers’ from CORS preflight channel).

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8000/api/user/register. (Reason: CORS request did not succeed).

Source map error: request failed with status 404
Resource URL: http://127.0.0.1:8002/js/app.js
Source Map URL: popper.js.map

Update2: I tried changing my axios post request:

const dataofuser={
    name:data.name,
    email:data.email,
    password:data.password
}


 const instance = axios.create({
     method:'post',
     baseURL: 'http://localhost:8000/api/user/',
     timeout: 1000,
     headers: {'Access-Control-Allow-Origin': '*' , 'Access-Control-Allow-Methods ':  'POST, GET, OPTIONS, PUT, DELETE','Access-Control-Allow-Headers':  'Content-Type, X-Auth-Token, Origin, Authorization','X-Requested-With': 'XMLHttpRequest' }
 });

instance          .post("register/create",dataofuser)
           .then(response => {
            console.log(response);
            return response;
          })
           .then(json => {
            if (json.data.success) {
              alert(`Registration Successful!`);
              history.push('/')

... But, then I get

invalid header name


Original As I have mentioned before in another post, I am currently self study React and Laravel. I am trying to build a basic registration app with React as front-end and Laravel as back-end. This post is about errors coming when I fill out the registration form and try to submit it; the user doesn't gets registered and I get several errors depending on what I am trying.

If I try:

axios
          .post("http://localhost:8000/api/user/register", {
              name: data.name,
              email: data.email,
              password: data.password
          })

I get in console :

> Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8000/api/user/register. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

>Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8000/api/user/register. (Reason: CORS request did not succeed).

>Source map error: request failed with status 404
Resource URL: http://127.0.0.1:8000/js/app.js
Source Map URL: popper.js.map

>[object Object] Error: Network Error 

If I try

axios
  .post("/user/register", {
      name: data.name,
      email: data.email,
      password: data.password
  })

then I get(I believe this an error regarding bad route definition ):

Source map error: request failed with status 404
Resource URL: http://127.0.0.1:8000/js/app.js
Source Map URL: popper.js.map

If I use

axios
  .post("/api/user/register", {
      name: data.name,
      email: data.email,
      password: data.password
  })

I get:

Source map error: request failed with status 404
Resource URL: http://127.0.0.1:8000/js/app.js
Source Map URL: popper.js.map

[object Object] Error: Request failed with status code 500

I am not sure anymore(I cannot clarify) if I have a CORS problem (although I have taken actions that should prevent such issues) or some other, routing or data-passing or just syntactic issue. I would go for the CORS problem(altough I have no idea what that popper.js.map notification is all about). I am posting the code below.


Update1:

I just ran my code in Chrome browser with the

 axios
          .post("http://localhost:8000/api/user/register", {
              name: data.name,
              email: data.email,
              password: data.password
          })

and I got the

Access to XMLHttpRequest at 'http://localhost:8000/api/user/register' from origin 'http://127.0.0.1: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.
app.js:70270 [object Object] Error: Network Error
app.js:371 POST http://localhost:8000/api/user/register net::ERR_FAILED

It seems I have a CORS problem... and I cant understand from yesterday and all day today how to handle it.


Some code:

My App(parent) component contains a function that gets passed to a Register(child) component; this function handles the registration process

        import React, {Component} from 'react'
        import ReactDOM from 'react-dom'
        import {BrowserRouter, Route, Switch } from 'react-router-dom'
        // import {Link} from 'react-router-dom'
        import Header from './Header'
        import Intro from './Intro'
        import Register from './Register'
        import Login from './Login'
        import Userpage from './Userpage'
        import Footer from './Footer'
        import Science from './Science'
        import Literature from './Literature'
        // import {AppState} from 'react-native'


        class App extends Component {

            constructor(props){
                super(props);
                this.state={
                    isLoggedIn:false,
                    user:{},
                    data_user:'',
                    data_username:''
                }

                this.username_Callback=this.username_Callback.bind(this)
                this._registerUser=this._registerUser.bind(this)



            }

            componentDidMount() {
                let state = localStorage["appState"];
                if (state) {
                  let AppState = JSON.parse(state);
                  console.log(AppState);
                  this.setState({ isLoggedIn: AppState.isLoggedIn, user: AppState });
                }
          }

            _registerUser(data){

                $("#email-login-btn")
                  .attr("disabled", "disabled")
                  .html(
                    '<i class="fa fa-spinner fa-spin fa-1x fa-fw"></i><span class="sr-only">Loading...</span>'
                  );



                // var formData = new FormData(); 
                // formData.append('data.name');
                // formData.append('data.email');
                // formData.append('data.password');

                console.log(data)
                console.log(data.name)
                console.log(data.email)
                console.log(data.password)
                // console.log(formData)



                axios
                  .post("http://localhost:8000/api/user/register", {
                      name: data.name,
                      email: data.email,
                      password: data.password
                  })
                  .then(response => {
                    console.log(response);
                    return response;
                  })
                  .then(json => {
                    if (json.data.success) {
                      alert(`Registration Successful!`);
                      history.push('/')


                      let userData = {
                        name: json.data.data.name,
                        id: json.data.data.id,
                        email: json.data.data.email,
                        auth_token: json.data.data.auth_token,
                        timestamp: new Date().toString()
                      };
                      let appState = {
                        isLoggedIn: true,
                        user: userData
                      };
                      // save app state with user date in local storage
                      localStorage["appState"] = JSON.stringify(appState);
                      this.setState({
                        isLoggedIn: appState.isLoggedIn,
                        user: appState.user
                      });
                    } else {
                      alert(`Registration Failed!`);
                      $("#email-login-btn")
                        .removeAttr("disabled")
                        .html("Register");
                    }
                  })
                  .catch(error => {
                    alert("An Error Occured!" + error);
                    console.log(`${data} ${error}`);
                    $("#email-login-btn")
                      .removeAttr("disabled")
                      .html("Register");
                  });

          };



render(){
                return(


                    <BrowserRouter>

                        <Header listNameFromParent={this.state.data_username} />

                        <Footer />

                        <Switch>
                            <Route exact path='/' component={Intro} />
                            <Route path='/register' render={props=><Register {...props} registerUser={this._registerUser}/>}/>

                            <Route path='/login' render={props=><Login {...props} loginUser={this._loginUser}/>}/>
                            <Route path='/userpage' component={Userpage}/>
                            <Route path='/science' component={Science}/>
                            <Route path='/literature' component={Literature}/>

                        </Switch>


                    </BrowserRouter>




                    )
            }
        }

        ReactDOM.render(<App />, document.getElementById('app'))

My Register component just contains the form and returns the input data. Using a console.log command, I am verifying that the data are indeed available in my App and inside my register function. If asked, I can post the code.

At my back-end I have:

api.php

<?php

        use Illuminate\Http\Request;

        // header('Access-Control-Allow-Origin: *');
        // //Access-Control-Allow-Origin: *
        // header('Access-Control-Allow-Methods:  POST, GET, OPTIONS, PUT, DELETE');
        // header('Access-Control-Allow-Headers:  Content-Type, X-Auth-Token, Origin, Authorization');
        /*
        |--------------------------------------------------------------------------
        | API Routes
        |--------------------------------------------------------------------------
        |
        | Here is where you can register API routes for your application. These
        | routes are loaded by the RouteServiceProvider within a group which
        | is assigned the "api" middleware group. Enjoy building your API!
        |
        */

        Route::middleware('auth:api')->get('/user', function (Request $request) {
            return $request->user();
        });


        Route::group(['middleware' => ['jwt.auth','api-header']], function () {

            // all routes to protected resources are registered here  
            Route::get('users/list', function(){
                $users = App\User::all();

                $response = ['success'=>true, 'data'=>$users];
                return response()->json($response, 201);
            });
        });
        Route::group(['middleware' => 'api-header'], function () {

            // The registration and login requests doesn't come with tokens 
            // as users at that point have not been authenticated yet
            // Therefore the jwtMiddleware will be exclusive of them
            Route::post('/user/login', 'UserController@login');
            Route::post('/user/register', 'UserController@register');
        });

API.php(middleware)

<?php

        namespace App\Http\Middleware;

        use Closure;

        class API
        {
            /**
             * Handle an incoming request.
             *
             * @param  \Illuminate\Http\Request  $request
             * @param  \Closure  $next
             * @return mixed
             */
            public function handle($request, Closure $next)
            {
                $response = $next($request);
                $response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Content-Range, Content-Disposition, Content-Description, X-Auth-Token');
                $response->header('Access-Control-Allow-Origin','*');
                $response->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
                $response->header('Access-Control-Allow-Headers',' Origin, Content-Type, Accept, Authorization, X-Request-With');
                $response->header('Access-Control-Allow-Credentials',' true');
                //add more headers here
                return $response;
            }
        }

UserController

<?php

    namespace App\Http\Controllers;

    use Illuminate\Http\Request;
    use App\User;
    use JWTAuth;
    use JWTAuthException;


    class UserController extends Controller
    {
        private function getToken($email, $password)
        {
            $token = null;
            //$credentials = $request->only('email', 'password');
            try {
                if (!$token = JWTAuth::attempt( ['email'=>$email, 'password'=>$password])) {
                    return response()->json([
                        'response' => 'error',
                        'message' => 'Password or email is invalid',
                        'token'=>$token
                    ]);
                }
            } catch (JWTAuthException $e) {
                return response()->json([
                    'response' => 'error',
                    'message' => 'Token creation failed',
                ]);
            }
            return $token;
        }
public function register(Request $request)
        { 


            $validator = Validator::make($request->all(), [
                'name' => 'required|max:255',
                'email' => 'required',
                'password' => 'required'
            ]);
            if ($validator->fails()) {
                return response()->json(['errors'=>$validator->errors()],422);
            }





            $payload = [
                'password'=>\Hash::make($request->password),
                'email'=>$request->email,
                'name'=>$request->name,
                'auth_token'=> ''
            ];




            $user = new \App\User($payload);
            if ($user->save())
            {

                $token = self::getToken($request->email, $request->password); // generate user token

                if (!is_string($token))  return response()->json(['success'=>false,'data'=>'Token generation failed'], 201);

                $user = \App\User::where('email', $request->email)->get()->first();

                $user->auth_token = $token; // update user token

                $user->save();

                $response = ['success'=>true, 'data'=>['name'=>$user->name,'id'=>$user->id,'email'=>$request->email,'auth_token'=>$token]];        
            }
            else
                $response = ['success'=>false, 'data'=>'Couldnt register user'];


            return response()->json($response, 201);
        }
    }

So again, I cannot clarify what the issue is exactly and the registration procedure doesn't work.

  • One of the posts I have read on this site: https://stackoverflow.com/questions/48943840/post-request-by-axios-to-laravel-giving-500-error . But I cannot fix my issue. – Constantine Black Aug 28 '19 at 14:54
  • A part of the code comes from this tutorial on medium: https://medium.com/@Gbxnga/token-based-authentication-with-react-and-laravel-restful-api-83f16581e85 – Constantine Black Aug 28 '19 at 15:57
  • ": I tried changing my axios post request:" — Don't add **response** headers to the request, you'll just make it preflighted and thus make the problem worse. – Quentin Aug 30 '19 at 12:34
  • @Quentin Yes, just figured that out and updated my post. So I should remove what exactly? Thanks. – Constantine Black Aug 30 '19 at 12:36
  • `headers: {'Access-Control-Allow-Origin': '*' , 'Access-Control-Allow-Methods ': 'POST, GET, OPTIONS, PUT, DELETE','Access-Control-Allow-Headers': 'Content-Type, X-Auth-Token, Origin, Authorization','X-Requested-With': 'XMLHttpRequest' }` – Quentin Aug 30 '19 at 12:36
  • @Quentin Yes, I got that, thanks. I meant should I remove them from my middleware also? – Constantine Black Aug 30 '19 at 12:38
  • No. The middleware needs to give permission via CORS. It's the client that can't give itself permission. – Quentin Aug 30 '19 at 12:39
  • @Quentin One final question: can my request tagged as preflighted due to apache2 configuration? Thank you for your time. – Constantine Black Aug 30 '19 at 12:41
  • 1
    No. A request is determined to be preflighted or simple entirely based on the content of the request (and by the browser) before anything is sent to the server. – Quentin Aug 30 '19 at 12:45

3 Answers3

4

Preflight requests are an HTTP-OPTIONS request made by a client to the server to verify that it supports CORS protocol.

https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request

The way to go is to register a route in your server that returns a response with necessary access control policy headers. For example:

Route::options('/{path}', function() {
  return response('', 200)
      ->header(
        'Access-Control-Allow-Headers', 
        'Origin, Content-Type, Content-Range, Content-Disposition, Content-Description, X-Auth-Token, X-Requested-With')
      ->header('Access-Control-Allow-Methods', 'POST, GET, PUT, OPTIONS, DELETE')
      ->header('Access-Control-Allow-Origin','*')
      ->header('Access-Control-Allow-Credentials',' true');
})->where('path', '.*');

This is similar to the middleware approach in your question except that the middleware is attached to routes but provides no fallback for options request to those routes.

Oluwafemi Sule
  • 27,776
  • 1
  • 40
  • 64
  • Thank you for the answer. You mean I should use this in place of my other routes in my api.php file or with them? – Constantine Black Aug 30 '19 at 17:23
  • `Access-Control-Allow-Headers` returned in the preflight response must include all headers used in the request. The error message in your question shows that `X-Requested-With` header is missing. – Oluwafemi Sule Aug 30 '19 at 19:10
  • I agree. But where should corrections happen? At middleware, apache2 server, api or web routes, somewhere in kernel, at axios, at the call of csrf token of laravel? Thank you. – Constantine Black Aug 31 '19 at 08:22
  • This should happen in Laravel considering __Update 3__ in your question. – Oluwafemi Sule Aug 31 '19 at 09:11
0

I will post here how I figured out what was happening with my Laravel-React-Axios simple-yet registration app. From the above posts that tried to tackle with the problem only some comments by ClueMediator were helpful on how to understand the issue behind the scenes and some informative-useful comments by Quentin on my post. I have to note here, that as far as I have seen, all the other posts and github discussion about axios and laravel and .js front-end didn't focus on this at all(or if they did, they did it without putting any serious attention to it).

First of all, one can see in my first post that changing the post request from localhost:8000 to just the name of the api/route seemed to have as an effect the removal of some of the cors; since I was running the project at one or two servers at a time, the cors could come back as a function of where artisan was serving the app.

Second of all, an internal 500 error was showing up even is the cases where the cors had been reduced; that was due to mistakes in the tutorial I was learning from and had to do with incomplete construction of the migrations.

To the point. It seems that axios is always trying to send, together with the request I was having in my code(a post request in this example) an options request. What I did to check out the problem was to change my host and port as artisan was serving and together my code to match the route correctly. Then, checking the network tab at console, I saw that although the post request was send fine, I still had cors because the axios.options request was going by default to the localhost:8000, but I wasn't serving my app from there. Specifying artisan to serve from there, and correcting the migrations construction, solved the problem.

It is important to underline something here: artisan serves at localhost:8000 the first time you open him/ but opening a second server, it goes on the next available port as normally would. Where not for the mistakes in the migrations code, I would have probably caught the problem originated from artisan and where it was serving. But, 'cause of a malfunctioning relation with my database, even then, I was getting a 500 error as internal server error, which had me opening another server, and then discovering that axios always sends an options request only to localhost:8000, even if my front-end and back end agreed on one another, having many changes to the code produced inconsistencies. So, in conclusion, after dealing with bad code(something I should have seen faster than I did), it was only a matter of correctly setting the axios and artisan to agreement.

Any observations on how the above logic could improve would be great.

Thanks to all.

  • Let me also note that I don't know if this could be a more general community wiki, where many of the problems related with laravel-js-axios could be collected. – Constantine Black Sep 01 '19 at 07:57
0

have you tried using the csrf token as a post parameter? if not what you can do is, set the csrf token and token in post parameters of your axios request. It will solve your issue.

Kiran Maniya
  • 5,637
  • 4
  • 34
  • 53
  • The issue has been solved. The comments by ClueMediator where helpful for a better understanding and I have also posted where the problem was found. Certainly this was not csrf problem. Thanks anyway. – Constantine Black Sep 02 '19 at 06:29