ctf演练

tech2023-07-27  112

ctf演练

If you ever developed for an API, you might have had troubles with changes of your database schema. If you didn’t happen to have a good implementation, you had to rework your whole model when changing some column names. In this article I will demonstrate how you can use Fractal as a layer between your models and JSON output. This post will show you how this package will make API development easier.

如果您曾经为API开发过,则可能在更改数据库架构时遇到了麻烦。 如果您碰巧没有很好的实现,则在更改某些列名称时必须重新设计整个模型。 在本文中,我将演示如何将Fractal用作模型和JSON输出之间的层。 这篇文章将向您展示该程序包将如何简化API开发。

分形包装 (Fractal as a package)

Fractal is a project written in PHP, and part of The League of Extraordinary Packages. These packages all comply to several requirements, like usage of PHP-FIG and Unit Test coverage. Fractal is mainly developed by Phil Sturgeon and still receives regular improvements. It can also be used with Composer.

Fractal是一个用PHP编写的项目,是The Extraordinary Packages的一部分 。 这些软件包都符合几个要求,例如PHP-FIG的使用和单元测试的范围。 Fractal主要由Phil Sturgeon开发,并且仍会定期进行改进。 它也可以与Composer一起使用。

搭建环境 (Setting up an environment)

For demonstration purposes, I will now set up a framework using Silex and Illuminate/Database (the ORM component of Laravel). It doesn’t matter if you don’t have any experience with one of those. The things I will do are very straightforward and I will explain them as much as I can. If something is unclear do not hesitate to leave a comment.

为了演示,我现在将使用Silex和Illuminate / Database(Laravel的ORM组件)建立一个框架。 如果您没有其中任何一个经验,那都没有关系。 我要做的事情非常简单,我将尽我所能解释。 如果有不清楚的地方,请立即发表评论。

I will now set up a framework. Note that if you don’t want to follow the steps, you can download all the code at the end of the article. Now start with creating a new folder inside root folder. We’ll start with creating our composer.json file, with all the dependencies that we need. In our case: Silex and Illuminate\Database. Create a composer.json file like this:

我现在将建立一个框架。 请注意,如果您不想执行这些步骤,则可以在文章末尾下载所有代码 。 现在开始在根文件夹中创建一个新文件夹。 我们将从创建我们的composer.json文件以及所需的所有依赖关系开始。 在我们的例子中:Silex和Illuminate \ Database。 创建一个composer.json文件,如下所示:

{ "require": { "silex/silex": "~1.2", "illuminate/database": "*" }, }

Install the packages with composer install.

使用composer install安装软件包。

数据库 (The database)

I will take the example of an online music database. The database will provide information for several songs: the songname, the artist name, artist website, album name, release date and music label. In the beginning this will all be in one table. If you want to try it out yourself download the file 1.sql from this article’s repository and run it on your database.

我将以在线音乐数据库为例。 该数据库将提供几首歌曲的信息:歌曲名称,艺术家名称,艺术家网站,专辑名称,发行日期和音乐标签。 一开始,所有这些都将放在一个表中。 如果您想尝试一下, 1.sql从本文的存储库下载文件1.sql ,然后在数据库上运行它。

代码 (The code)

In order to use Silex with Illuminate\Database, we’ll need some code. Inside the app folder create a new file index.php. This is where we’ll start Silex, connect to the database and define routes:

为了在Illuminate \ Database中使用Silex,我们需要一些代码。 在app folder创建一个新文件index.php 。 这是我们启动Silex,连接数据库并定义路由的地方:

<?php require("../vendor/autoload.php"); $app = new Silex\Application(); $app['database'] = require("database.php"); $app->mount('/tracks', include 'controllers/tracks.php'); $app->run();

On the first line we require the autoload file for composer. Then we create a new Silex application and load in Illuminate/Database. We then create a controller for /tracks so that all URLs which start with /tracks will be handled by controllers/tracks.php. The database.php file looks like this, don’t forget to change the connection settings:

在第一行中,我们需要供作曲家使用的自动加载文件。 然后,我们创建一个新的Silex应用程序并将其加载到Illuminate/Database 。 然后,我们为/tracks创建一个控制器,以便所有以/tracks开头的URL将由controllers/tracks.php处理。 database.php文件如下所示,不要忘记更改连接设置:

<?php use Illuminate\Database\Capsule\Manager as Capsule; $capsule = new Capsule; $capsule->addConnection([ 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'musicstore', 'username' => 'root', 'password' => '', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', ]); use Illuminate\Events\Dispatcher; use Illuminate\Container\Container; $capsule->setEventDispatcher(new Dispatcher(new Container)); $capsule->setAsGlobal(); $capsule->bootEloquent(); return $capsule;

第一个API版本 (First API version)

Initially, the database only contains one table (import 1.sql). This table contains information about the track itself (title, music label), the album (title and release date), and the artist (name and website). Our API will provide two things: when navigating to /tracks, it will output a json list with the track ids, their title and artist. When navigating to /tracks/$id, it will output all the track information as a json object.

最初,数据库仅包含一个表(导入1.sql )。 该表包含有关曲目本身(标题,音乐标签),专辑(标题和发行日期)以及艺术家(名称和网站)的信息。 我们的API将提供两件事:导航到/tracks ,它将输出带有轨道ID,其标题和艺术家的json列表。 导航到/tracks/$id ,它将所有轨道信息输出为json对象。

曲目列表 (Tracklisting)

We begin with the list of tracks. Put this code comes into /controllers/tracks.php:

我们从曲目列表开始。 把这段代码放到/controllers/tracks.php :

<?php $tracks = $app['controllers_factory']; $tracks->get('/', function() use ($app) { $tracklist = Track::getTrackList(); $output = array("data" => $tracklist); return json_encode($output); }); return $tracks;

and this code for the model, /models/Track.php:

以及该代码的模型/models/Track.php :

public static function getTrackList() { return Track::select('id','name','artist_name')->get(); }

This uses the Illuminate\Database querybuilder to get a list of id’s, names and articles and then outputs them. The output will have this format:

这使用Illuminate \ Database querybuilder获取ID,名称和文章的列表,然后输出它们。 输出将具有以下格式:

{ data: [ { id: 1, name: "Song 1", artist_name: "Artist 1" }, { id: 2, name: "Song 2", artist_name: "Artist 2" } ] }

This works as expected, but if the table schema changes, so will the JSON output. We will now use a Fractal transformer to force a specific structure of output. This Transformer is a class that extends Fractal\TransformerAbstract. Create a new folder for transformers in your app folder with the name transformers. The first transformer is TracklistTransformer.php which we’ll use solely for /tracks, and it looks like this:

这可以按预期工作,但是如果表架构更改,则JSON输出也会更改。 现在,我们将使用分形变压器强制输出的特定结构。 该Transformer是扩展Fractal\TransformerAbstract 。 在您的应用文件夹中为变压器创建一个新文件夹,名称为transformers 。 第一个转换器是TracklistTransformer.php ,我们仅将其用于/tracks ,它看起来像这样:

<?php namespace Musicstore; class TracklistTransformer extends \League\Fractal\TransformerAbstract { public function transform(Track $track) { return [ 'id' => (int) $track->id, 'title' => $track->title, 'artist' => $track->artist_name ]; } }

Every transformer requires a transform() method, with its argument being the object you want to transform, in our case an instance of the class Track. In the return statement the structure is defined as an associative array.

每个转换器都需要一个transform()方法,其参数是要转换的对象,在本例中是Track类的实例。 在return语句中,该结构定义为关联数组。

We’ll now apply the transformation and create the output array by using a Serializer. We are about to use the DataArraySerializer because this will put our data under the data-key in the JSON object. This allows you to have other information in your response like status codes or error messages. In order to use this serializer, we’ll need an instance of the manager class. I will use the DI container for this so that we don’t have to rewrite boilerplate code every time. The new /controllers/tracks.php file looks like this:

现在,我们将应用转换并通过使用Serializer创建输出数组。 我们将要使用DataArraySerializer因为这会将我们的数据放在JSON对象中的data-key下。 这使您可以在响应中包含其他信息,例如状态代码或错误消息。 为了使用此序列化程序,我们需要一个manager类的实例。 我将为此使用DI容器,这样我们就不必每次都重写样板代码。 新的/controllers/tracks.php文件如下所示:

<?php $tracks = $app['controllers_factory']; $tracks->get('/', function() use ($app) { $tracks = Track::getTrackList(); $tracklist = new \League\Fractal\Resource\Collection($tracks, new TracklistTransformer); $output = $app['serializer']->createData($tracklist)->toArray(); return json_encode($output); }); return $tracks;

We use $tracklist = new \League\Fractal\Resource\Collection($tracks, new TracklistTransformer); to apply the transformation. In this route we are using a collection of tracks: our $tracks variable is an object of the class Illuminate\Database\Eloquent\Collection. This class is compatible with Fractal and its transformers. For the other route we’re going to use a transformer on a single table item. But more on that later.

我们使用$tracklist = new \League\Fractal\Resource\Collection($tracks, new TracklistTransformer); 应用转换。 在此路线中,我们使用轨道的集合:我们的$tracks变量是Illuminate \ Database \ Eloquent \ Collection类的对象。 此类与Fractal及其变压器兼容。 对于另一条路线,我们将在单个表项上使用一个转换器。 但是稍后会更多。

For using $app['serializer'], place this code in index.php, right under the app['database'] statement:

对于使用$app['serializer'] ,请将以下代码放在index.php ,在app['database']语句的正下方:

$app['serializer'] = $app->share(function() { $manager = new \League\Fractal\Manager(); $manager->setSerializer(new League\Fractal\Serializer\DataArraySerializer()); return $manager; });

Now you have a working tracklisting.

现在,您有了工作清单。

跟踪信息 (Track information)

I will now quickly demonstrate how to show information about specific tracks and then make edits to the data schema with Fractal. Open controllers/tracks.php and add the following code above the return statement:

现在,我将快速演示如何显示有关特定轨道的信息,然后使用Fractal对数据模式进行编辑。 打开controllers/tracks.php并在return语句上方添加以下代码:

<?php $tracks->get('/{id}', function($id) use ($app) { return Track::find($id); });

Track::find($id) returns an object of class Track with its id matching the URL. Again this works but it’s not a good way, so we will implement Fractal. Create a new transformer transformers/TrackTransformer.php:

Track::find($id)返回一个Track类的对象,其ID与URL相匹配。 再次可行,但这不是一个好方法,因此我们将实现Fractal。 创建一个新的转换器transformers/TrackTransformer.php :

<?php namespace Musicstore; class TrackTransformer extends \League\Fractal\TransformerAbstract { public function transform(Track $track) { return [ 'id' => (int) $track->id, 'title' => $track->title, 'artist_name' => $track->artist_name, 'artist_website' => $track->artist_website, 'album_name' => $track->album_name, 'album_release' => $track->album_release, 'album_label' => $track->album_label ]; } }

In your controller edit the route to this:

在您的控制器中,编辑到此的路线:

$tracks->get('/{id}', function($id) use ($app) { $track = Track::find($id); $track = new \League\Fractal\Resource\Item($track, new TrackTransformer); $output = $app['serializer']->createData($track)->toArray(); return json_encode($output); });

As you can see it is rather similar to the previous route, except that in this case we’re not dealing with a Collection but a single Item. If you are using 1.sql you can navigate to localhost/app/tracks/1 and see some sample data.

如您所见,它与先前的路线非常相似,除了在这种情况下,我们不处理Collection而是单个Item 。 如果使用1.sql ,则可以导航到localhost/app/tracks/1并查看一些示例数据。

更新的API版本 (Updated API version)

Our music store wants to expand and provide more information about the artists and the albums. In the new database schema there are separate tables for the artists and the albums. You can download 2.sql to use the new schema. The tables look like this:

我们的音乐商店希望扩展并提供有关艺术家和专辑的更多信息。 在新的数据库模式中,有艺术家和专辑的单独表。 您可以下载2.sql以使用新架构。 这些表如下所示:

TRACKS:

轨迹:

id

ID title

标题 artist_id

artist_id album_id

album_id

ARTISTS:

艺术家:

id

ID name

名称 website

网站

ALBUMS:

相册:

id

ID name

名称 release

释放 label

标签

In the tracks table, the artist and album columns are integers which correspond to rows in the respective tables. We’ll use the tranformers to keep our output structure the same. In order to make things easier we’ll use some features of Illuminate\Database. Go inside models/Track.php and add two more methods:

在曲目表中,艺术家和专辑列是整数,分别对应于各个表中的行。 我们将使用变形器来保持我们的输出结构不变。 为了使事情变得容易,我们将使用Illuminate\Database某些功能。 进入models/Track.php并添加另外两个方法:

public function artist() { return $this->belongsTo('\Musicstore\Artist'); } public function album() { return $this->belongsTo('\Musicstore\Album'); }

If we have a Track object we can access its Album and Artist by using $track->album and $track->artist. First we need to make those models so create two files models/Artist.php and models/Album.php:

如果我们有Track对象,则可以使用$track->album和$track->artist来访问其Album和Artist 。 首先,我们需要制作这些模型,因此创建两个文件models/Artist.php和models/Album.php :

// models/Artist.php <?php namespace Musicstore; class Artist extends \Illuminate\Database\Eloquent\Model { public function tracks() { return $this->hasMany('\Musicstore\Track'); } } // models/Album.php <?php namespace Musicstore; class Album extends \Illuminate\Database\Eloquent\Model { public function tracks() { return $this->hasMany('\Musicstore\Track'); } }

The last thing we have to do now is edit our transformers:

现在我们要做的最后一件事是编辑我们的转换器:

// transformers/TrackTransformer.php return [ 'id' => (int) $track->id, 'title' => $track->title, 'artist_name' => $track->artist->name, 'artist_website' => $track->artist->website, 'album_name' => $track->album->name, 'album_release' => $track->album->release, 'album_label' => $track->album->label ]; // transformers/TracklistTransformer.php return [ 'id' => (int) $track->id, 'title' => $track->title, 'artist' => $track->artist->name ];

If you test everything out, you’ll see that it works.

如果您对所有内容进行了测试,就会发现它可以正常工作。

结论 (Conclusion)

As I’ve demonstrated, you can, by adding Fractal before your output, easily perform database changes, without the end user noticing. You can download the full example code here at my repository. If something is unclear please do not hesitate to leave a comment.

如我所演示的,您可以通过在输出之前添加Fractal来轻松地执行数据库更改,而无需最终用户注意。 您可以在我的存储库中下载完整的示例代码。 如果有不清楚的地方,请随时发表评论。

翻译自: https://www.sitepoint.com/fractal-practical-walkthrough/

ctf演练

最新回复(0)