Apibase Controllernot Found Error Try Again

With the rise of mobile evolution and JavaScript frameworks, using a RESTful API is the all-time option to build a unmarried interface between your information and your customer.

Laravel is a PHP framework developed with PHP programmer productivity in listen. Written and maintained past Taylor Otwell, the framework is very opinionated and strives to save developer time by favoring convention over configuration. The framework also aims to evolve with the web and has already incorporated several new features and ideas in the web development world—such as task queues, API authentication out of the box, real-time communication, and much more.

Laravel API Tutorial - Building a RESTful Web service

In this tutorial, we'll explore the ways you can build—and examination—a robust API using Laravel with authentication. Nosotros'll exist using Laravel 5.4, and all of the lawmaking is bachelor for reference on GitHub.

RESTful APIs

Starting time, we need to understand what exactly is considered a RESTful API. Remainder stands for REpresentational Land Transfer and is an architectural style for network communication between applications, which relies on a stateless protocol (usually HTTP) for interaction.

HTTP Verbs Represent Actions

In RESTful APIs, we use the HTTP verbs equally actions, and the endpoints are the resources acted upon. We'll exist using the HTTP verbs for their semantic significant:

  • GET: retrieve resources
  • Mail: create resource
  • PUT: update resources
  • DELETE: delete resources
HTTP verbs: GET, POST, PUT and DELETE are actions in RESTful APIs

Update Activeness: PUT vs. POST

RESTful APIs are a affair of much contend and there are enough of opinions out there on whether is best to update with Mail service, PATCH, or PUT, or if the create action is all-time left to the PUT verb. In this article nosotros'll exist using PUT for the update action, as according to the HTTP RFC, PUT means to create/update a resources at a specific location. Another requirement for the PUT verb is idempotence, which in this example basically ways yous tin can send that asking 1, 2 or 1000 times and the upshot will be the aforementioned: one updated resource in the database.

Resources

Resources will be the targets of the deportment, in our instance Manufactures and Users, and they have their ain endpoints:

  • /manufactures
  • /users

In this laravel api tutorial, the resource will accept a 1:ane representation on our data models, but that is non a requirement. You can have resource represented in more than i data model (or not represented at all in the database) and models completely off limits for the user. In the end, you get to decide how to architect resources and models in a mode that is fitting to your application.

A Notation on Consistency

The greatest reward of using a set of conventions such every bit Remainder is that your API will exist much easier to consume and develop effectually. Some endpoints are pretty straightforward and, equally a result, your API will be much more easier to use and maintain every bit opposed to having endpoints such as GET /get_article?id_article=12 and POST /delete_article?number=40. I've congenital terrible APIs like that in the past and I still hate myself for information technology.

However, at that place will be cases where it volition be hard to map to a Create/Retrieve/Update/Delete schema. Retrieve that the URLs should not comprise verbs and that resources are not necessarily rows in a table. Another affair to keep in listen is that you don't have to implement every action for every resources.

Setting Up a Laravel Web Service Project

Equally with all modern PHP frameworks, we'll need Composer to install and handle our dependencies. After you lot follow the download instructions (and add together to your path environs variable), install Laravel using the command:

          $ composer global crave laravel/installer                  

After the installation finishes, you tin can scaffold a new awarding like this:

          $ laravel new myapp                  

For the above control, you lot demand to have ~/composer/vendor/bin in your $PATH. If you don't want to deal with that, yous tin also create a new project using Composer:

          $ composer create-project --adopt-dist laravel/laravel myapp                  

With Laravel installed, you should be able to first the server and test if everything is working:

          $ php artisan serve Laravel development server started: <http://127.0.0.1:8000>                  
When you open localhost:8000 on your browser, you should see the Laravel sample page

When you open up localhost:8000 on your browser, y'all should see this sample page.

Migrations and Models

Before actually writing your first migration, make sure y'all accept a database created for this app and add together its credentials to the .env file located in the root of the project.

          DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secret                  

You can besides utilize Homestead, a Vagrant box specially crafted for Laravel, only that is a chip out of the scope of this article. If you'd like to know more, refer to the Homestead documentation.

Allow'south get started with our showtime model and migration—the Article. The article should have a title and a body field, as well as a cosmos date. Laravel provides several commands through Artisan—Laravel's command line tool—that help the states by generating files and putting them in the correct folders. To create the Commodity model, nosotros can run:

          $ php artisan brand:model Commodity -yard                  

The -m option is curt for --migration and information technology tells Artisan to create ane for our model. Here's the generated migration:

          <?php  use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Pattern; apply Illuminate\Database\Migrations\Migration;  class CreateArticlesTable extends Migration {     /**      * Run the migrations.      *      * @return void      */     public part upwards()     {         Schema::create('articles', function (Pattern $table) {             $tabular array->increments('id');             $table->timestamps();         });     }      /**      * Contrary the migrations.      *      * @return void      */     public office downwardly()     {         Schema::dropIfExists('articles');     } }                  

Let's dissect this for a 2d:

  • The upwardly() and down() methods will be run when we migrate and rollback respectively;
  • $table->increments('id') sets up an motorcar incrementing integer with the proper noun id;
  • $tabular array->timestamps() will set up the timestamps for us—created_at and updated_at, but don't worry about setting a default, Laravel takes intendance of updating these fields when needed.
  • And finally, Schema::dropIfExists() will, of course, drop the table if it exists.

With that out of the fashion, permit's add two lines to our upwards() method:

          public function upward() {     Schema::create('manufactures', office (Blueprint $table) {         $tabular array->increments('id');         $table->string('title');         $table->text('body');         $table->timestamps();     }); }                  

The string() method creates a VARCHAR equivalent cavalcade while text() creates a TEXTequivalent. With that washed, permit'south get ahead and migrate:

          $ php artisan migrate                  

You lot can also use the --step option here, and it volition separate each migration into its own batch so that you tin can scroll them dorsum individually if needed.

Laravel out of the box comes with ii migrations, create_users_table and create_password_resets_table. We won't be using the password_resets table, but having the users table ready for u.s.a. will be helpful.

Now allow'south go dorsum to our model and add those attributes to the $fillable field so that we tin can use them in our Article::create and Article::update models:

          class Article extends Model {     protected $fillable = ['title', 'trunk']; }                  

Fields inside the $fillable belongings can be mass assigned using Eloquent's create() and update() methods. You tin can as well employ the $guarded property, to allow all merely a few properties.

Database Seeding

Database seeding is the process of filling upwardly our database with dummy information that nosotros can employ to test information technology. Laravel comes with Faker, a dandy library for generating just the right format of dummy data for u.s.. And then allow'south create our first seeder:

          $ php artisan brand:seeder ArticlesTableSeeder                  

The seeders will be located in the /database/seeds directory. Here's how it looks like afterward we set up it upward to create a few articles:

          class ArticlesTableSeeder extends Seeder {     public function run()     {         // Permit'south truncate our existing records to start from scratch.         Article::truncate();          $faker = \Faker\Manufacturing plant::create();          // And now, let's create a few manufactures in our database:         for ($i = 0; $i < 50; $i++) {             Article::create([                 'title' => $faker->sentence,                 'body' => $faker->paragraph,             ]);         }     } }                  

And so let'due south run the seed command:

          $ php artisan db:seed --class=ArticlesTableSeeder                  

Allow's repeat the process to create a Users seeder:

          class UsersTableSeeder extends Seeder {     public function run()     {         // Let's clear the users table first         User::truncate();          $faker = \Faker\Mill::create();          // Allow's make certain everyone has the aforementioned password and          // let'southward hash information technology before the loop, or else our seeder          // will be also wearisome.         $password = Hash::make('toptal');          User::create([             'name' => 'Administrator',             'email' => 'admin@test.com',             'password' => $password,         ]);          // And at present let's generate a few dozen users for our app:         for ($i = 0; $i < 10; $i++) {             User::create([                 'name' => $faker->name,                 'email' => $faker->email,                 'password' => $password,             ]);         }     } }                  

We can go far easier past calculation our seeders to the main DatabaseSeeder class within the database/seeds binder:

          form DatabaseSeeder extends Seeder {     public part run()     {         $this->phone call(ArticlesTableSeeder::form);         $this->phone call(UsersTableSeeder::class);     } }                  

This style, we can simply run $ php artisan db:seed and information technology will run all the called classes in the run() method.

Routes and Controllers

Allow's create the basic endpoints for our application: create, recollect the list, retrieve a single ane, update, and delete. On the routes/api.php file, we can simply exercise this:

          Utilize App\Article;   Route::get('articles', function() {     // If the Content-Type and Accept headers are set to 'application/json',      // this volition return a JSON construction. This will be cleaned up after.     return Article::all(); });   Route::get('articles/{id}', function($id) {     return Article::detect($id); });  Road::post('manufactures', function(Request $asking) {     return Article::create($request->all); });  Route::put('manufactures/{id}', function(Request $request, $id) {     $article = Article::findOrFail($id);     $article->update($asking->all());      return $commodity; });  Route::delete('articles/{id}', function($id) {     Article::discover($id)->delete();      render 204; })                  

The routes inside api.php will be prefixed with /api/ and the API throttling middleware will be automatically practical to these routes (if you want to remove the prefix you can edit the RouteServiceProvider course on /app/Providers/RouteServiceProvider.php).

Now allow's move this code to its own Controller:

          $ php artisan make:controller ArticleController                  

ArticleController.php:

          use App\Article;   class ArticleController extends Controller {     public office index()     {         return Article::all();     }       public part evidence($id)     {         return Article::find($id);     }      public function store(Request $request)     {         return Commodity::create($request->all());     }      public role update(Request $request, $id)     {         $article = Commodity::findOrFail($id);         $article->update($request->all());          return $commodity;     }      public function delete(Request $request, $id)     {         $article = Article::findOrFail($id);         $article->delete();          render 204;     } }                  

The routes/api.php file:

          Road::get('articles', 'ArticleController@index'); Road::get('manufactures/{id}', 'ArticleController@prove'); Route::mail('articles', 'ArticleController@store'); Road::put('articles/{id}', 'ArticleController@update'); Route::delete('articles/{id}', 'ArticleController@delete');                  

We can amend the endpoints past using implicit route model bounden. This fashion, Laravel volition inject the Commodity instance in our methods and automatically return a 404 if it isn't constitute. Nosotros'll have to make changes on the routes file and on the controller:

          Route::get('articles', 'ArticleController@index'); Route::go('articles/{article}', 'ArticleController@testify'); Route::post('articles', 'ArticleController@store'); Route::put('articles/{article}', 'ArticleController@update'); Road::delete('articles/{article}', 'ArticleController@delete');                  
          class ArticleController extends Controller {     public office index()     {         return Article::all();     }      public function prove(Article $article)     {         render $article;     }      public function store(Asking $asking)     {         $article = Commodity::create($request->all());          render response()->json($article, 201);     }      public part update(Request $asking, Commodity $article)     {         $article->update($asking->all());          return response()->json($article, 200);     }      public function delete(Article $article)     {         $article->delete();          return response()->json(null, 204);     } }                  

A Note on HTTP Status Codes and the Response Format

We've also added the response()->json() call to our endpoints. This lets u.s.a. explicitly render JSON data besides as send an HTTP code that can be parsed by the client. The about common codes you'll be returning will be:

  • 200: OK. The standard success code and default pick.
  • 201: Object created. Useful for the shop actions.
  • 204: No content. When an action was executed successfully, but there is no content to return.
  • 206: Partial content. Useful when you accept to return a paginated list of resources.
  • 400: Bad request. The standard option for requests that fail to pass validation.
  • 401: Unauthorized. The user needs to be authenticated.
  • 403: Forbidden. The user is authenticated, but does not have the permissions to perform an action.
  • 404: Not plant. This volition be returned automatically by Laravel when the resource is not institute.
  • 500: Internal server error. Ideally you're not going to exist explicitly returning this, but if something unexpected breaks, this is what your user is going to receive.
  • 503: Service unavailable. Pretty self explanatory, but besides another code that is not going to be returned explicitly by the application.

Sending a Correct 404 Response

If you tried to fetch a non-existent resources, you'll be thrown an exception and yous'll receive the whole stacktrace, like this:

NotFoundHttpException Stacktrace

We can ready that past editing our exception handler course, located in app/Exceptions/Handler.php, to return a JSON response:

          public office render($request, Exception $exception) {     // This will supervene upon our 404 response with     // a JSON response.     if ($exception instanceof ModelNotFoundException) {         return response()->json([             'error' => 'Resource non found'         ], 404);     }      render parent::render($request, $exception); }                  

Here'south an instance of the return:

          {     data: "Resource not found" }                  

If y'all're using Laravel to serve other pages, you accept to edit the lawmaking to work with the Accept header, otherwise 404 errors from regular requests will return a JSON as well.

          public function render($request, Exception $exception) {     // This volition replace our 404 response with     // a JSON response.     if ($exception instanceof ModelNotFoundException &&         $asking->wantsJson())     {         render response()->json([             'data' => 'Resource not constitute'         ], 404);     }      return parent::render($request, $exception); }                  

In this instance, the API requests will need the header Accept: application/json.

Authentication

There are many ways to implement API Authentication in Laravel (one of them existence Passport, a great way to implement OAuth2), but in this commodity, we'll take a very simplified arroyo.

To get started, nosotros'll need to add an api_token field to the users table:

          $ php artisan brand:migration --table=users adds_api_token_to_users_table                  

And so implement the migration:

          public function up() {     Schema::table('users', part (Blueprint $table) {         $table->string('api_token', threescore)->unique()->nullable();     }); }  public part down() {     Schema::table('users', office (Blueprint $table) {         $table->dropColumn(['api_token']);     }); }                  

After that, but run the migration using:

          $ php artisan drift                  

Creating the Register Endpoint

We'll make use of the RegisterController (in the Auth folder) to return the right response upon registration. Laravel comes with hallmark out of the box, but we still demand to tweak it a bit to return the response we want.

If APIs were in English, this is what an api authentication conversation would sound like

The controller makes use of the trait RegistersUsers to implement the registration. Here's how it works:

          public office annals(Request $request) {     // Here the request is validated. The validator method is located     // inside the RegisterController, and makes sure the name, email     // password and password_confirmation fields are required.     $this->validator($asking->all())->validate();      // A Registered upshot is created and will trigger any relevant     // observers, such as sending a confirmation email or any      // lawmaking that needs to exist run as soon as the user is created.     result(new Registered($user = $this->create($asking->all())));      // Later on the user is created, he's logged in.     $this->guard()->login($user);      // And finally this is the hook that nosotros want. If there is no     // registered() method or information technology returns nothing, redirect him to     // another URL. In our case, nosotros but need to implement     // that method to return the correct response.     return $this->registered($asking, $user)                     ?: redirect($this->redirectPath()); }                  

We just demand to implement the registered() method in our RegisterController. The method receives the $asking and the $user, so that's really all we want. Here's how the method should look like inside the controller:

          protected function registered(Request $request, $user) {     $user->generateToken();      return response()->json(['data' => $user->toArray()], 201); }                  

And we can link it on the routes file:

          Route::post('register', 'Auth\RegisterController@register');                  

In the department to a higher place, we used a method on the User model to generate the token. This is useful so that we only accept a unmarried way of generating the tokens. Add the following method to your User model:

          class User extends Authenticatable {     ...     public function generateToken()     {         $this->api_token = str_random(60);         $this->save();          return $this->api_token;     } }                  

And that'south it. The user is now registered and cheers to Laravel's validation and out of the box authentication, the name, email, password, and password_confirmation fields are required, and the feedback is handled automatically. Checkout the validator() method within the RegisterController to see how the rules are implemented.

Hither's what we get when nosotros hit that endpoint:

          $ curl -X Mail service http://localhost:8000/api/register \  -H "Accept: awarding/json" \  -H "Content-Type: awarding/json" \  -d '{"name": "John", "electronic mail": "john.doe@toptal.com", "countersign": "toptal123", "password_confirmation": "toptal123"}'                  
          {     "data": {         "api_token":"0syHnl0Y9jOIfszq11EC2CBQwCfObmvscrZYo5o2ilZPnohvndH797nDNyAT",         "created_at": "2017-06-20 21:17:15",         "electronic mail": "john.doe@toptal.com",         "id": 51,         "name": "John",         "updated_at": "2017-06-20 21:17:15"     } }                  

Creating a Login Endpoint

Just similar the registration endpoint, we can edit the LoginController (in the Auth folder) to support our API authentication. The login method of the AuthenticatesUsers trait tin exist overridden to back up our API:

          public function login(Request $request) {     $this->validateLogin($request);      if ($this->attemptLogin($asking)) {         $user = $this->guard()->user();         $user->generateToken();          return response()->json([             'data' => $user->toArray(),         ]);     }      return $this->sendFailedLoginResponse($request); }                  

And we tin can link information technology on the routes file:

          Route::post('login', 'Auth\LoginController@login');                  

Now, assuming the seeders take been run, hither's what we get when we ship a POST asking to that route:

          $ curl -10 POST localhost:8000/api/login \   -H "Accept: application/json" \   -H "Content-type: application/json" \   -d "{\"email\": \"admin@test.com\", \"countersign\": \"toptal\" }"                  
          {     "data": {         "id":1,         "proper name":"Administrator",         "email":"admin@test.com",         "created_at":"2017-04-25 01:05:34",         "updated_at":"2017-04-25 02:l:40",         "api_token":"Jll7q0BSijLOrzaOSm5Dr5hW9cJRZAJKOzvDlxjKCXepwAeZ7JR6YP5zQqnw"     } }                  

To transport the token in a request, you lot can practice it by sending an aspect api_token in the payload or as a bearer token in the request headers in the form of Authorization: Bearer Jll7q0BSijLOrzaOSm5Dr5hW9cJRZAJKOzvDlxjKCXepwAeZ7JR6YP5zQqnw.

Logging Out

With our current strategy, if the token is wrong or missing, the user should receive an unauthenticated response (which we'll implement in the next section). So for a simple logout endpoint, we'll ship in the token and it will be removed on the database.

routes/api.php:

          Route::post('logout', 'Auth\LoginController@logout');                  

Auth\LoginController.php:

          public function logout(Request $request) {     $user = Auth::baby-sit('api')->user();      if ($user) {         $user->api_token = null;         $user->salvage();     }      render response()->json(['data' => 'User logged out.'], 200); }                  

Using this strategy, whatever token the user has volition be invalid, and the API volition deny access (using middlewares, every bit explained in the next section). This needs to exist coordinated with the front end-end to avoid the user remaining logged without having access to any content.

Using Middlewares to Restrict Access

With the api_token created, we can toggle the authentication middleware in the routes file:

          Route::middleware('auth:api')     ->go('/user', function (Asking $request) {         render $request->user();     });                  

We tin can access the current user using the $request->user() method or through the Auth facade

          Auth::guard('api')->user(); // instance of the logged user Auth::guard('api')->check(); // if a user is authenticated Auth::guard('api')->id(); // the id of the authenticated user                  

And we get a result like this:

An InvalidArgumentException Stacktrace

This is because nosotros need to edit the current unauthenticated method on our Handler class. The current version returns a JSON only if the asking has the Take: application/json header, and so let'south change information technology:

          protected function unauthenticated($request, AuthenticationException $exception) {     render response()->json(['error' => 'Unauthenticated'], 401); }                  

With that fixed, we tin can go back to the article endpoints to wrap them in the auth:api middleware. We can do that by using route groups:

          Road::group(['middleware' => 'auth:api'], function() {     Road::get('manufactures', 'ArticleController@alphabetize');     Route::get('articles/{article}', 'ArticleController@testify');     Route::mail service('articles', 'ArticleController@store');     Route::put('articles/{article}', 'ArticleController@update');     Route::delete('articles/{article}', 'ArticleController@delete'); });                  

This way we don't have to set the middleware for each of the routes. It doesn't save a lot of time right now, but every bit the project grows it helps to keep the routes Dry out.

Testing Our Endpoints

Laravel includes integration with PHPUnit out of the box with a phpunit.xml already prepare. The framework also provides u.s. with several helpers and extra assertions that makes our lives much easier, particularly for testing APIs.

There are a number of external tools you can use to test your API; still, testing inside Laravel is a much better culling—we tin have all the benefits of testing an API structure and results while retaining full control of the database. For the list endpoint, for case, we could run a couple of factories and affirm the response contains those resources.

To go started, we'll need to tweak a few settings to use an in-memory SQLite database. Using that will brand our tests run lightning fast, but the trade-off is that some migration commands (constraints, for example) will not work properly in that particular setup. I propose moving away from SQLite in testing when you start getting migration errors or if you prefer a stronger prepare of tests instead of performant runs.

Nosotros'll also run the migrations earlier each test. This setup will allow us to build the database for each examination then destroy information technology, avoiding any type of dependency betwixt tests.

In our config/database.php file, nosotros'll demand to fix the database field in the sqlite configuration to :retentiveness::

          ... 'connections' => [      'sqlite' => [         'driver' => 'sqlite',         'database' => ':memory:',         'prefix' => '',     ],          ... ]                  

Then enable SQLite in phpunit.xml by adding the environs variable DB_CONNECTION:

                      <php>         <env proper noun="APP_ENV" value="testing"/>         <env proper name="CACHE_DRIVER" value="array"/>         <env name="SESSION_DRIVER" value="array"/>         <env proper noun="QUEUE_DRIVER" value="sync"/>         <env name="DB_CONNECTION" value="sqlite"/>     </php>                  

With that out of the way, all that's left is configuring our base of operations TestCase form to employ migrations and seed the database before each test. To do so, nosotros demand to add the DatabaseMigrations trait, and and so add an Artisan call on our setUp() method. Hither'southward the grade after the changes:

          use Illuminate\Foundation\Testing\DatabaseMigrations; apply Illuminate\Foundation\Testing\TestCase as BaseTestCase; utilize Illuminate\Support\Facades\Artisan;  abstract class TestCase extends BaseTestCase {     utilize CreatesApplication, DatabaseMigrations;      public role setUp()     {         parent::setUp();         Artisan::phone call('db:seed');     } }                  

Ane last thing that I similar to do is to add the exam command to composer.json:

                      "scripts": {         "test" : [             "vendor/bin/phpunit"         ],     ...      },                  

The test control will be available like this:

          $ composer exam                  

Setting Upward Factories for Our Tests

Factories will allow us to quickly create objects with the right data for testing. They're located in the database/factories binder. Laravel comes out of the box with a factory for the User grade, so permit's add one for the Article class:

          $factory->define(App\Article::form, function (Faker\Generator $faker) {     return [         'championship' => $faker->sentence,         'body' => $faker->paragraph,     ]; });                  

The Faker library is already injected to assistance us create the correct format of random data for our models.

Our First Tests

We can apply Laravel's affirm methods to easily hitting an endpoint and evaluate its response. Permit'south create our offset test, the login test, using the post-obit control:

          $ php artisan brand:exam Characteristic/LoginTest                  

And here is our test:

          grade LoginTest extends TestCase {     public function testRequiresEmailAndLogin()     {         $this->json('POST', 'api/login')             ->assertStatus(422)             ->assertJson([                 'email' => ['The e-mail field is required.'],                 'password' => ['The password field is required.'],             ]);     }       public part testUserLoginsSuccessfully()     {         $user = manufacturing plant(User::class)->create([             'email' => 'testlogin@user.com',             'password' => bcrypt('toptal123'),         ]);          $payload = ['email' => 'testlogin@user.com', 'password' => 'toptal123'];          $this->json('Mail service', 'api/login', $payload)             ->assertStatus(200)             ->assertJsonStructure([                 'data' => [                     'id',                     'name',                     'email',                     'created_at',                     'updated_at',                     'api_token',                 ],             ]);      } }                  

These methods examination a couple of unproblematic cases. The json() method hits the endpoint and the other asserts are pretty self explanatory. I detail about assertJson(): this method converts the response into an array searches for the argument, so the social club is of import. You lot can chain multiple assertJson() calls in that instance.

Now, permit's create the annals endpoint test and write a couple for that endpoint:

          $ php artisan make:test RegisterTest                  
          class RegisterTest extends TestCase {     public function testsRegistersSuccessfully()     {         $payload = [             'name' => 'John',             'email' => 'john@toptal.com',             'countersign' => 'toptal123',             'password_confirmation' => 'toptal123',         ];          $this->json('post', '/api/register', $payload)             ->assertStatus(201)             ->assertJsonStructure([                 'information' => [                     'id',                     'name',                     'email',                     'created_at',                     'updated_at',                     'api_token',                 ],             ]);;     }      public part testsRequiresPasswordEmailAndName()     {         $this->json('post', '/api/annals')             ->assertStatus(422)             ->assertJson([                 'proper name' => ['The name field is required.'],                 'email' => ['The email field is required.'],                 'password' => ['The password field is required.'],             ]);     }      public function testsRequirePasswordConfirmation()     {         $payload = [             'name' => 'John',             'e-mail' => 'john@toptal.com',             'password' => 'toptal123',         ];          $this->json('post', '/api/register', $payload)             ->assertStatus(422)             ->assertJson([                 'countersign' => ['The password confirmation does not friction match.'],             ]);     } }                  

And lastly, the logout endpoint:

          $ php artisan make:test LogoutTest                  
          class LogoutTest extends TestCase {     public function testUserIsLoggedOutProperly()     {         $user = factory(User::class)->create(['email' => 'user@test.com']);         $token = $user->generateToken();         $headers = ['Authorization' => "Bearer $token"];          $this->json('go', '/api/articles', [], $headers)->assertStatus(200);         $this->json('post', '/api/logout', [], $headers)->assertStatus(200);          $user = User::discover($user->id);          $this->assertEquals(zippo, $user->api_token);     }      public function testUserWithNullToken()     {         // Simulating login         $user = factory(User::form)->create(['email' => 'user@test.com']);         $token = $user->generateToken();         $headers = ['Authorisation' => "Bearer $token"];          // Simulating logout         $user->api_token = null;         $user->save();          $this->json('get', '/api/articles', [], $headers)->assertStatus(401);     } }                  

It's of import to note that, during testing, the Laravel awarding is not instantiated once again on a new request. Which ways that when nosotros hitting the hallmark middleware, it saves the electric current user inside the TokenGuard case to avoid hit the database once again. A wise pick, withal—in this example, information technology ways we have to split the logout test into two, to avert any issues with the previously buried user.

Testing the Commodity endpoints is straightforward besides:

          class ArticleTest extends TestCase {     public role testsArticlesAreCreatedCorrectly()     {         $user = factory(User::class)->create();         $token = $user->generateToken();         $headers = ['Authorization' => "Bearer $token"];         $payload = [             'title' => 'Lorem',             'body' => 'Ipsum',         ];          $this->json('Post', '/api/articles', $payload, $headers)             ->assertStatus(200)             ->assertJson(['id' => i, 'championship' => 'Lorem', 'trunk' => 'Ipsum']);     }      public role testsArticlesAreUpdatedCorrectly()     {         $user = factory(User::class)->create();         $token = $user->generateToken();         $headers = ['Potency' => "Bearer $token"];         $commodity = manufactory(Article::form)->create([             'title' => 'First Commodity',             'body' => 'Offset Torso',         ]);          $payload = [             'title' => 'Lorem',             'torso' => 'Ipsum',         ];          $response = $this->json('PUT', '/api/manufactures/' . $commodity->id, $payload, $headers)             ->assertStatus(200)             ->assertJson([                  'id' => 1,                  'title' => 'Lorem',                  'torso' => 'Ipsum'              ]);     }      public function testsArtilcesAreDeletedCorrectly()     {         $user = factory(User::course)->create();         $token = $user->generateToken();         $headers = ['Authorization' => "Bearer $token"];         $article = factory(Commodity::grade)->create([             'championship' => 'First Article',             'body' => 'Showtime Trunk',         ]);          $this->json('DELETE', '/api/articles/' . $commodity->id, [], $headers)             ->assertStatus(204);     }      public part testArticlesAreListedCorrectly()     {         manufacturing plant(Commodity::class)->create([             'title' => 'First Article',             'body' => 'Showtime Trunk'         ]);          manufacturing plant(Article::course)->create([             'title' => '2nd Article',             'body' => '2nd Body'         ]);          $user = factory(User::class)->create();         $token = $user->generateToken();         $headers = ['Potency' => "Bearer $token"];          $response = $this->json('Get', '/api/articles', [], $headers)             ->assertStatus(200)             ->assertJson([                 [ 'title' => 'First Commodity', 'body' => 'First Body' ],                 [ 'title' => 'Second Commodity', 'body' => 'Second Body' ]             ])             ->assertJsonStructure([                 '*' => ['id', 'trunk', 'title', 'created_at', 'updated_at'],             ]);     }  }                  

Side by side Steps

That's all in that location is to it. There's definitely room for improvement—you can implement OAuth2 with the Passport package, integrate a pagination and transformation layer (I recommend Fractal), the list goes on—but I wanted to go through the basics of creating and testing an API in Laravel with no external packages.

Laravel evolution has certainly improved my experience with PHP and the ease of testing with it has solidified my interest in the framework. It's not perfect, but it's flexible enough to permit you work effectually its issues.

If you're designing a public API, check out 5 Golden Rules for Smashing Web API Design.

carterparepithe.blogspot.com

Source: https://www.toptal.com/laravel/restful-laravel-api-tutorial

0 Response to "Apibase Controllernot Found Error Try Again"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel