alexcicioc/swagger-router

处理路由和请求/响应验证,这些在OpenApi规范中定义

v1.1.7 2020-02-18 08:06 UTC

README

Swagger Router 是一个库,在文档化的同时加速API开发。

它使用与 OpenAPI规范2.0 兼容的json规范文件,应该支持大多数功能。如果您发现缺少某些功能,请随时打开一个pull request或问题。

通过composer安装

composer require alexcicioc/swagger-router

使用swagger-router引导API

使用swagger定义端点

您需要在使用swagger规范中定义您的API端点。这里有一个简单的示例您可以使用(为了可读性,它是yaml格式的,但应该转换为json)

建议:您可以使用 Swagger Editor 安全地修改规范,然后点击文件菜单并“转换并保存到JSON”。

swagger: '2.0'
info:
  version: 0.0.1
  title: Courses API
basePath: /api
schemes:
  - http
  - https
consumes:
  - application/json
produces:
  - application/json
paths:
  /courses:
    x-swagger-router-controller: Courses # used to route to the php controller class
    get:
      description: Returns a list of courses
      operationId: getCourses # used to route to the php controller method
      parameters:
        - $ref: '#/parameters/limitQuery'
        - $ref: '#/parameters/startIndexQuery'
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/CourseList'
        default:
          description: Error
          schema:
            $ref: '#/definitions/ErrorResponse'
  '/courses/{courseId}':
    x-swagger-router-controller: Courses
    get:
      description: Returns a course
      operationId: getCourse # used to route to the php controller method
      parameters:
        - $ref: '#/parameters/courseIdPath'
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/Course'
        '404':
          description: Not Found
        default:
          description: Error
          schema:
            $ref: '#/definitions/ErrorResponse'
            
definitions:

  CourseList:
    properties:
      results:
        type: array
        items:
          $ref: '#/definitions/Course'
    required:
      - results
      
  Course:
    properties:
      id:
        description: Course id
        type: integer
        minimum: 1
      title:
        description: Course title
        type: string
        maxLength: 255
      shortDescription:
        description: Short description
        type: string
        maxLength: 255
      longDescription:
        description: Long description
        type: string
      status:
        description: Course status
        type: string
        enum:
          - not-started
          - in-progress
          - complete
        default: not-started
    required:
      - id
      - title
      - shortDescription
      - longDescription
      - status
  
  ErrorResponse:
    required:
      - message
    properties:
      message:
        description: Error Message
        type: string
        
parameters:

  courseIdPath:
    in: path
    name: courseId
    type: integer
    minimum: 1
    required: true
    
  limitQuery:
    in: query
    name: limit
    type: integer
    default: 10
    
  startIndexQuery:
    in: query
    name: startIndex
    type: integer
    default: 0

重定向请求

您需要将所有内容重定向到index.php。如果您使用Apache,可以将以下内容添加到您的 .htaccess 文件中

RewriteEngine on
RewriteRule ^(.*)$ index.php [NC,L,QSA]

使用swagger-router引导应用

在index.php中,您必须构建SwaggerRouter实例。您可以使用此示例

try {
    $app = new SwaggerRouter();
    // Validates and extracts the information from your swagger spec
    $app->use(new SpecParser('/app/swagger.json')); # Path to your spec
    // Optional - Handles the /swagger endpoint that exposes the spec to frontend apps
    $app->use(new SwaggerRawHandler());
    // Validates the called route, http method and content type
    $app->use(new RouteValidator());
    // Handles extracting the parameters from the request and formatting them
    $app->use(new ParamsHandler());
    // Optional - Validates the OAuth2 token given in the Authorization header
    $app->use(new OAuth(AuthorizationFactory::makeResourceServer()));
    // Optional - Handles validating the request parameters
    $app->use(new RequestValidator());
    // Routes the request to it's specific controller (given by x-swagger-router-controller)
    $app->use(new Router('\App\Api\Controllers')); # Controllers namespace (must be PSR-4 compliant)
    // Handles formatting the response
    $app->use(new ResponseHandler());
    // Optional - Handles validating the response
    $app->use(new ResponseValidator());

    $swaggerRequest = SwaggerRequest::fromGlobals(); // extends PSR-7 ServerRequest
    $swaggerResponse = new SwaggerResponse(); // extends PSR-7 Response
    $response = $app($swaggerRequest, $swaggerResponse);
    $response->send();
} catch (HttpException $e) {
    (new SwaggerResponse())
        ->withStatus($e->getCode())
        ->body((object)['message' => $e->getMessage()])
        ->send();
}

创建控制器

之前在规范中我们添加了这些行

x-swagger-router-controller: Courses
operationId: getCourses

这告诉swagger-router搜索Courses控制器并调用getCourses方法

namespace App\Api\Controllers;

use Alexcicioc\SwaggerRouter\SwaggerRequest;
use Alexcicioc\SwaggerRouter\SwaggerResponse;

class Courses
{
    public function getCourses(SwaggerRequest $request, SwaggerResponse $response): SwaggerResponse
    {
        $filters = $request->getParam('technology');
        $limit = $request->getParam('limit');
        $startIndex = $request->getParam('startIndex');

        $results = [];
        // do db stuff here
        
        return $response
                ->withStatus(200)
                ->body((object)['results' => $results]);

    }
    
    public function getCourse(SwaggerRequest $request, SwaggerResponse $response): SwaggerResponse
    {
        $courseId = $request->getParam('courseId');
        
        $course = new \stdClass();
        $course->id = $courseId;
        // db stuff here
        
        return $response
                ->withStatus(200)
                ->body($course);
    }
}

示例规范

https://github.com/alexcicioc/swagger-router/blob/master/sample-spec.json

Laravel 兼容性

Swagger Router的中间件目前与Laravel不兼容,但有一个解决方案。

这是一个示例Laravel中间件,它调用swagger router(未经适当测试,可能会有一些副作用)

<?php

namespace App\Http\Middleware;

use Alexcicioc\SwaggerRouter\Exceptions\HttpException;
use Alexcicioc\SwaggerRouter\Middlewares\ParamsHandler;
use Alexcicioc\SwaggerRouter\Middlewares\RequestValidator;
use Alexcicioc\SwaggerRouter\Middlewares\ResponseHandler;
use Alexcicioc\SwaggerRouter\Middlewares\ResponseValidator;
use Alexcicioc\SwaggerRouter\Middlewares\Router;
use Alexcicioc\SwaggerRouter\Middlewares\RouteValidator;
use Alexcicioc\SwaggerRouter\Middlewares\SpecParser;
use Alexcicioc\SwaggerRouter\Middlewares\SwaggerRawHandler;
use Alexcicioc\SwaggerRouter\SwaggerRequest;
use Alexcicioc\SwaggerRouter\SwaggerResponse;
use Alexcicioc\SwaggerRouter\SwaggerRouter;
use Closure;
use Illuminate\Http\Request;

class LaravelSwaggerRouter
{
    public function handle(Request $request, Closure $next)
    {
        try {
            $app = new SwaggerRouter();
            // Validates and extracts the information from your swagger spec
            $app->use(new SpecParser('/var/www/php/specs/spec.json')); # Path to your spec
            // Optional - Handles the /swagger endpoint that exposes the spec to frontend apps
            $app->use(new SwaggerRawHandler());
            // Validates the called route, http method and content type
            $app->use(new RouteValidator());
            // Handles extracting the parameters from the request and formatting them
            $app->use(new ParamsHandler());
            // Optional - Handles validating the request parameters
            $app->use(new RequestValidator());
            // Routes the request to it's specific controller (given by x-swagger-router-controller)
            $app->use(new Router('\App\Http\Controllers')); # Controllers namespace (must be PSR-4 compliant)
            // Handles formatting the response
            $app->use(new ResponseHandler());
            // Optional - Handles validating the response
            $app->use(new ResponseValidator());

            $swaggerRequest = SwaggerRequest::fromGlobals();

            $swaggerResponse = new SwaggerResponse(); // extends PSR-7 Response
            $response = $app($swaggerRequest, $swaggerResponse);

            return response()->json($response->rawBody, $response->getStatusCode(), $response->getHeaders());
        } catch (HttpException $e) {
            return response()->json((object)['message' => $e->getMessage()], $e->getCode());
        }
    }
}