Building Sample App with RESTful API using JPA and Flyway

spring-restful-jpa-flyway

Movie,actor & director RESTful API.  Sample app with jpa, flyway and testcontainers.

Docker

DB

alt text

Download details:
Author: mangila
Source code: https://github.com/mangila/spring-restful-jpa-flyway
License: MIT license

#spring #java #springboot #restful

Building Sample App with RESTful API using JPA and Flyway
Rupert  Beatty

Rupert Beatty

1659136320

Api-guard: A Simple Way Of Authenticating Your RESTful APIs

ApiGuard

This package is no longer maintained

This package is no longer maintained as Laravel already has a similar feature built-in since Laravel 5.8 and Laravel 6.x. Documentation on Laravel's API authentication can be found here. Furthermore, since Laravel 7.x, there is a new Laravel package called Airlock/Sanctum that would better serve API authentication purposes.

A simple way of authenticating your APIs with API keys using Laravel. This package uses the following libraries:

Laravel 5.3, 5.4 and 5.5 is finally supported!

**Laravel 5.3.x onwards: ~4.*

**Laravel 5.1.x to 5.2.x: ~3.*

**Laravel 5.1.x: ~2.*

**Laravel 4.2.x: ~1.* (Recently updated version for Laravel 4. Please note that there are namespace changes here)

**Laravel 4.2.x: 0.* (The version that most of you are using)

Quick start

Installation for Laravel 5.3 to 5.4

Run composer require chrisbjr/api-guard 4.*

In your config/app.php add Chrisbjr\ApiGuard\Providers\ApiGuardServiceProvider to the end of the providers array

'providers' => array(

    ...
    Chrisbjr\ApiGuard\Providers\ApiGuardServiceProvider::class,
),

Now publish the migration and configuration files for api-guard:

$ php artisan vendor:publish --provider="Chrisbjr\ApiGuard\Providers\ApiGuardServiceProvider"

Then run the migration:

$ php artisan migrate

It will setup api_keys table.

Generating your first API key

Once you're done with the required setup, you can now generate your first API key.

Run the following command to generate an API key:

php artisan api-key:generate

Generally, the ApiKey object is a polymorphic object meaning this can belong to more than one other model.

To generate an API key that is linked to another object (a "user", for example), you can do the following:

+php artisan api-key:generate --id=1 --type="App\User"

To specify that a model can have API keys, you can attach the Apikeyable trait to the model:

use Chrisbjr\ApiGuard\Models\Mixins\Apikeyable;

class User extends Model
{
    use Apikeyable;

    ...
}

This will attach the following methods to the model:

// Get the API keys of the object
$user->apiKeys();

// Create an API key for the object
$user->createApiKey();

To generate an API key from within your application, you can use the following method in the ApiKey model:

$apiKey = Chrisbjr\ApiGuard\Models\ApiKey::make()

// Attach a model to the API key
$apiKey = Chrisbjr\ApiGuard\Models\ApiKey::make($model)

Usage

You can start using ApiGuard by simply attaching the auth.apikey middleware to your API route:

Route::middleware(['auth.apikey'])->get('/test', function (Request $request) {
    return $request->user(); // Returns the associated model to the API key
});

This effectively secures your API with an API key which needs to specified in the X-Authorization header. This can be configured in config/apiguard.php.

Here is a sample cURL command to demonstrate:

curl -X GET \
  http://apiguard.dev/api/test \
  -H 'x-authorization: api-key-here'

You might also want to attach this middleware to your api middleware group in your app/Http/Kernel.php to take advantage of other Laravel features such as throttling.

/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    ...

    'api' => [
        'throttle:60,1',
        'bindings',
        'auth.apikey',
    ],
];

If you noticed in the basic example, you can also access the attached model to the API key by calling $request->user(). We are attaching the related model in this method because in most use cases, this is actually the user.

Unauthorized Requests

Unauthorized requests will get a 401 status response with the following JSON:

{
  "error": {
    "code": "401",
    "http_code": "GEN-UNAUTHORIZED",
    "message": "Unauthorized."
  }
}

ApiGuardController

The ApiGuardController takes advantage of Fractal and api-response libraries.

This enables us to easily create APIs with models and use transformers to give a standardized JSON response.

Here is an example:

Let's say you have the following model:

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    protected $fillable = [
        'name',
    ];
}

You can make a basic controller which will return all books like this:

use Chrisbjr\ApiGuard\Http\Controllers\ApiGuardController;
use App\Transformers\BookTransformer;
use App\Book;

class BooksController extends ApiGuardController
{
    public function all()
    {
        $books = Book::all();

        return $this->response->withCollection($books, new BookTransformer);
    }
}

Now, you'll need to make the transformer for your Book object. Transformers help with defining and manipulating the variables you want to return to your JSON response.

use League\Fractal\TransformerAbstract;
use App\Book;

class BookTransformer extends TransformerAbstract
{
    public function transform(Book $book)
    {
        return [
            'id'         => $book->id,
            'name'       => $book->name,
            'created_at' => $book->created_at,
            'updated_at' => $book->updated_at,
        ];
    }
}

Once you have this accessible in your routes, you will get the following response from the controller:

{
  "data": {
    "id": 1,
    "title": "The Great Adventures of Chris",
    "created_at": {
      "date": "2017-05-25 18:54:18",
      "timezone_type": 3,
      "timezone": "UTC"
    },
    "updated_at": {
      "date": "2017-05-25 18:54:18",
      "timezone_type": 3,
      "timezone": "UTC"
    }
  }
}

More examples can be found on the Github page: https://github.com/ellipsesynergie/api-response.

To learn more about transformers, visit the PHP League's documentation on Fractal: Fractal

API Validation Responses

ApiGuard comes with a request class that can handle validation of requests for you and throw a standard response.

You can create a Request class as you usually do but in order to get a standard JSON response you'll have to extend the ApiGuardFormRequest class.

use Chrisbjr\ApiGuard\Http\Requests\ApiGuardFormRequest;

class BookStoreRequest extends ApiGuardFormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'name' => 'required',
        ];
    }
}

Now you can use this in your controller as you normally do with Laravel:

use Chrisbjr\ApiGuard\Http\Controllers\ApiGuardController;
use App\Transformers\BookTransformer;
use App\Book;

class BooksController extends ApiGuardController
{
    public function store(BookStoreRequest $request)
    {
        // Request should already be validated

        $book = Book::create($request->all())

        return $this->response->withItem($book, new BookTransformer);
    }
}

If the request failed to pass the validation rules, it will return with a response like the following:

{
  "error": {
    "code": "GEN-UNPROCESSABLE",
    "http_code": 422,
    "message": {
      "name": [
        "The name field is required."
      ]
    }
  }
}

Author: Chrisbjr
Source Code: https://github.com/chrisbjr/api-guard 
License: MIT license

#laravel #api #restful

Api-guard: A Simple Way Of Authenticating Your RESTful APIs
Dexter  Goodwin

Dexter Goodwin

1657407840

KWDB: An Extensible RESTful Wrap Of LevelDB

This package is not actively maintained because my focus is on other projects, but if there is bugs, I will try to fix it immediately.

kwdb

This is a monorepo for kwdb and its new cli tool(WIP). For their code checkout the corresponding folder in the packages folder.

Package Status

Package NameInstall SizeDependency Statusnpm Version
kwdbinstall sizedependency statusnpm Version
kwdb-cliWIPWIPWIP

Author: Shanoaice
Source Code: https://github.com/shanoaice/kwdb 
License: MIT license

#javascript #leveldb #restful 

KWDB: An Extensible RESTful Wrap Of LevelDB

Bake: A Bake Template for RESTful CakePHP Controllers

MixerAPI Bake

Bake RESTful CakePHP controllers in seconds with this API focused bake template. 

Installation

!!! note "" You can skip this step if MixerAPI is installed.

composer require mixerapi/bake
bin/cake plugin load MixerApi/Bake

Alternatively after composer installing you can manually load the plugin in your Application:

# src/Application.php
public function bootstrap(): void
{
    // other logic...
    $this->addPlugin('MixerApi/Bake');
}

Usage

MixerApi/Bake will automatically detect the following plugins and adjust the bake output accordingly:

Add --theme MixerApi/Bake to your bake commands.

Bake all your controllers:

bin/cake bake controller all --theme MixerApi/Bake

Bake a single controller:

bin/cake bake controller {ControllerName} --theme MixerApi/Bake

Bake everything (theme only impacts controllers):

bin/cake bake all --everything --theme MixerApi/Bake

Read more at MixerAPI.com.

Author: Mixerapi
Source Code: https://github.com/mixerapi/bake 
License: View license

#php #api #restful #cakephp 

Bake: A Bake Template for RESTful CakePHP Controllers

Node-Restful | A library for Quickly Providing a REST API with Express or Connect

node-restful

Create awesome APIs using express.

Register mongoose resources and default RESTful routes are automatically made

var express = require('express'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    morgan = require('morgan'),
    restful = require('node-restful'),
    mongoose = restful.mongoose;
var app = express();

app.use(morgan('dev'));
app.use(bodyParser.urlencoded({'extended':'true'}));
app.use(bodyParser.json());
app.use(bodyParser.json({type:'application/vnd.api+json'}));
app.use(methodOverride());

mongoose.connect("mongodb://localhost/resources");

var Resource = app.resource = restful.model('resource', mongoose.Schema({
    title: String,
    year: Number,
  }))
  .methods(['get', 'post', 'put', 'delete']);

Resource.register(app, '/resources');

app.listen(3000);

Registers the following routes:

GET /resources
GET /resources/:id
POST /resources
PUT /resources/:id
DELETE /resources/:id

which do exactly what you think they do!

The best part is that restful.model returns a Mongoose model, so you can interact with it the same way that you're already accustomed to! (i.e. new Resource, Resource.findById, etc.)

Support

This library is currently supported through complaint driven development, so if you see something, have a feature request, open an issue and if it seems to jive with the mission of the library, I'll prioritize it.

Install

npm install node-restful

Usage

There is a good example application under examples/movies.

I will also show some features and use cases for them, how to set up routes, etc.

API

There are a few functions that are available after we register the mongoose schema. The first one we already saw.

.methods([...]) takes a list of methods that should be available on the resource. Future calls to methods will override previously set values To disallow delete operations, simply run

Resource.methods(['get', 'post', 'put'])

We can also run custom routes. We can add custom routes by calling .route(path, handler)

Resource.route('recommend', function(req, res, next) {
  res.send('I have a recommendation for you!');
});

This will set up a route at /resources/recommend, which will be called on all HTTP methods. We can also restrict the HTTP method by adding it to the path:

Resource.route('recommend.get', function(req, res, next) {
   res.send('GET a recommendation');
});
Resource.route('recommend', ['get', 'put', 'delete'], function(req, res, next) { ... });

Or do some combination of HTTP methods.

Now. Lets say we have to run arbitrary code before or after a route. Lets say we need to hash a password before a POST or PUT operation. Well, easy.

Resource.before('post', hash_password)
  .before('put', hash_password);
      
function hash_password(req, res, next) {
  req.body.password = hash(req.body.password);
  next();
}

Boy. That was easy. What about doing stuff after request, but before its sent back to the user? Restful stores the bundle of data to be returned in res.locals (see express docs). res.locals.status_code is the returned status code and res.locals.bundle is the bundle of data. In every before and after call, you are free to modify these are you see fit!

Resource.after('get', function(req, res, next) {
  var tmp = res.locals.bundle.title; // Lets swap the title and year fields because we're funny!
  res.locals.bundle.title = res.locals.bundle.year;
  res.locals.bundle.year = tmp;
  next(); // Don't forget to call next!
});
    
Resource.after('recommend', do_something); // Runs after all HTTP verbs

Now, this is all great. But up until now we've really only talked about defining list routes, those at /resources/route_name. We can also define detail routes. Those look like this

Resource.route('moreinfo', {
    detail: true,
    handler: function(req, res, next) {
        // req.params.id holds the resource's id
        res.send("I'm at /resources/:id/moreinfo!")
    }
});

I don't think this is the prettiest, and I'd be open to suggestions on how to beautify detail route definition...

And that's everything for now!

Built-in Filters

Node-restful accepts many options to manipulate the list results. These options can be added to your request either via the querystring or the POST body. They are passed into the mongoose query to filter your resultset.

Selecting the entity-properties you need

If you only need a few properties instead of the entire model, you can ask the service to only give just the properties you need:

A GET request to /users/?select=name%20email will result in:

[
    {
        "_id": "543adb9c7a0f149e3ac29438",
        "name": "user1",
        "email": "user1@test.com"
    },
    {
        "_id": "543adb9c7a0f149e3ac2943b",
        "name": "user2",
        "email": "user2@test.com"
    }
]

Limiting the number and skipping items

When implementing pagination you might want to use skip and limit filters. Both do exactly what their name says and just skip given amount of items or limit to a set amount of items.

/users/?limit=5 will give you the first 5 items
/users/?skip=5 will skip the first 5 and give you the rest
/users/?limit=5&skip=5 will skip the first 5 and then give you the second 5

Sorting the result

Getting a sorted list is as easy as adding a sort querystring parameter with the property you want to sort on. /users/?sort=name will give you a list sorted on the name property, with an ascending sort order.

Changing the sort order uses the same rules as the string notation of mongoose's sort filter. /users/?sort=-name will return the same list as before with a descending sort order.

Filtering the results

Sometimes you just want to get all people older than 18, or you are want to get all people living in a certain city. Then you would want to use filters for that. You can ask the service for equality, or values greater or less than, give it an array of values it should match to, or even a regex.

FilterQueryExampleDescription
equalequals/users?gender=male or /users?gender__equals=maleboth return all male users
not equalne/users?gender__ne=malereturns all users who are not male (female and x)
greater thangt/users?age__gt=18returns all users older than 18
greater than or equal togte/users?age__gte=18returns all users 18 and older (age should be a number property)
less thanlt/users?age__lt=30returns all users age 29 and younger
less than or equal tolte/users?age__lte=30returns all users age 30 and younger
inin/users?gender__in=female,malereturns all female and male users
ninnin/users?age__nin=18,30returns all users with age other than 18 or 30
Regexregex/users?username__regex=/^baugarten/ireturns all users with a username starting with baugarten

Populating a sub-entity

When you have setup a mongoose Schema with properties referencing other entities, you can ask the service to populate them for you.

A GET request to /users/542edff9fffc55dd29d99346 will result in:

{
    "_id": "542edff9fffc55dd29d99346",
    "name": "the employee",
    "email": "employee@company.com",
    "boss": "542edff9fffc55dd29d99343",
    "__v": 0
}

A GET request to /users/542edff9fffc55dd29d99346?populate=boss will result in:

{
    "_id": "542edff9fffc55dd29d99346",
    "name": "the employee",
    "email": "employee@company.com",
    "boss": {
        "_id": "542edff9fffc55dd29d99343",
        "name": "the boss",
        "email": "boss@company.com",
        "__v": 0
    },
    "__v": 0
}

Contributing

You can view the issue list for what I'm working on, or contact me to help!

Just reach out to me

MIT License

Copyright (c) 2012 by Ben Augarten

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Download Details:
 

Author: baugarten
Download Link: Download The Source Code
Official Website: https://github.com/baugarten/node-restful 
License: MIT License

#nodejs #restful #mongodb 

Node-Restful | A library for Quickly Providing a REST API with Express or Connect

How to Build the Backend with MongoDB, Express and Nodejs

In this tutorial, we'll build a RESTful API that implements the CRUD (Create, Read, Update, Delete) operations for an employee. For data persistence, we'll be using a MongoDB Atlas cluster.


Part 2: ☞ https://morioh.com/p/b970a38ef3db?f=60d94da986deb67e97e9ce2b


Timeline:
1:13  Finished Product 
2:30  MEAN Stack Architecture
3:55  Pre-requisites for tutorial series
4:30 Building the server-side Node and Express app
6:50 Installing dependencies
13:45 Creating an Employee Interface
15:00 Connecting to the database
25:00 Build the RESTFUL API
26:20 GET /employees
28:20 GET /employees/:id
31:30 PUT /employees/:id
32:45 DELETE /employees/:id
33:45 Register the routes

#restful #mongodb #express #node

How to Build the Backend with MongoDB, Express and Nodejs

EFコアとPostgresを使用した.NETコアのRESTfulAPI

REST API は、サーバーと通信するために複数のクライアント(またはAPPS)が使用できるアプリケーションプログラミングインターフェイスです。

Rest APIは、アプリケーションに必要なデータを便利な形式(JSONやXMLなど)で保存および取得する一種のWebサービスです。

ステートレスであるため、Webサービスにアクセスするために依存するコードライブラリを必要としないため、開発者に大きな柔軟性を提供します。

RESTでサポートされている多くのプロトコルの中で、最も一般的なものはHTTPです。

HTTPRequestを使用してクライアントから要求が送信されると、対応する応答がHTTPResponseを使用してサーバーから送信されます。リクエストとレスポンスでサポートされている最も広く使用されている機械可読形式は、JSON(Javascript Object Notification)とXML(Extensible Markup Language)です。

RESTは、コンピューター科学者のROYFIELDINGによって作成されました。

REST APIを使用して、さまざまなアクションを実行できます。アクションに基づいて、関連する方法を使用する必要があります。以下は、RESTでサポートされている5つの方法です。

  1. GET-このメソッドは、データベース/サーバーからデータを取得するために使用されます。
  2. POST-このメソッドは、新しいレコードを作成するために使用されます。
  3. PUT-このメソッドは、レコードを変更/置換するために使用されます。レコード全体を置き換えます。
  4. PATCH-このメソッドは、レコードを変更/更新するために使用されます。レコードの一部を置き換えます。
  5. DELETE-このメソッドは、レコードを削除するために使用されます。

EfCoreとPostgresを使用したdotnetCoreのRESTfulApi

これを例で見てみましょう。私たちは、母親が十分な休息をとることが決してないことを知っています。しかし、これらの休むことのないママを例として取り上げ、RestAPIをどのように使用するかを見てみましょう。:)

生まれたばかりの赤ちゃんによる過度の要求の中で、おむつはリーダーボードで最初に位置します。

母親は赤ちゃんのためにすべてが最善であることを望んでいます。ですから、母親が赤ちゃんに最適なおむつを選びたいと思うのは明らかです。そこで、彼女はショッピングWebサイト(フリップカートを想定)にアクセスして、おむつを検索します。これにより、すべてのおむつのリストを取得するために、フリップカートのサーバーにHTTPリクエストが送信されます。FlipkartのサーバーはHTTP応答で応答します。これは、いくつかの基本的な詳細を含むおむつのリストを含むJSONオブジェクト(想定)になります。FlipkartのWebサイトは、この応答を読み取り、人間が読める形式に変換して、母親が見ることができるようにWebページに表示します。

彼女が生まれたばかりの赤ちゃんのために特定のおむつを選び、それを彼女のリストに追加した後。これにより、POSTリクエストが作成され、おむつのブランド、サイズ、数量、価格などを含む新しいレコードがフリップカートのデータベースに作成されます。

彼女の赤ちゃんは成長を続け、すぐに新生児のサイズを超えます。母親がまだおむつのブランドを気に入っていて、サイズを大きくしたいとします。彼女がしなければならないのは、新しいおむつのサイズを選択することだけです。彼女がおむつのサイズを新生児のサイズからサイズ1に更新すると、これによりPATCHメソッドがトリガーされ、他のすべては同じままで、おむつのサイズのみが変更されます。

母親が現在のブランドを変更し、別のブランドに切り替えることを決定することは非常に一般的です。ここで、母親はPUTリクエストを開始します。ここで、以前に選択されたブランドを含むデータ全体が変更され、新しく選択されたブランドに対応するデータに置き換えられます。

最後に、いくつかのGET、POST、PUT、およびPATCHを含む一連の実験の後、母親が子供をトイレトレーニングする時が来ました。彼女が子供を訓練することに成功した場合、おむつはもはや必要ありません。これにより、 DELETE要求がトリガーされます。

前提条件

  1. Visual Studio 2022
  2. .Net Core 6:ASP.NET Coreは、Microsoftによって開発されたASP.NETの新しいバージョンです。これは、WebアプリケーションとAPIを開発するためのオープンソースフレームワークであり、Windows、Mac、またはLinuxで実行できます。Asp.Net Coreは、最新のクラウドベースのインターネット接続アプリケーションを構築するためのクロスプラットフォーム、高性能、オープンソースフレームワークです。
  3. Entity Frameworkコア:Entity Framework(EF)コアは、人気のあるEntityFrameworkデータアクセステクノロジの軽量で拡張可能な オープンソース のクロスプラットフォームバージョンです。
  4. Postgresデータベース:PostgreSQLは強力なオープンソースのオブジェクトリレーショナルデータベースシステムであり、30年以上にわたって活発に開発されており、信頼性、機能の堅牢性、パフォーマンスで高い評価を得ています。

API作成手順

ステップ1

Visual Studio 2022を開き、asp.netコアwebapiプロジェクトを作成します。

ステップ2

NugetからNpgsql.EntityFrameworkCore.PostgreSQLとMicrosoft.EntityFrameworkCore.Toolsをインストールします

ステップ3

Product.csとOrder.csを作成します

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("product")]
public class Product {
    [Key, Required]
    public int id {
        get;
        set;
    }
    [Required]
    public string ? name {
        get;
        set;
    }
    public string ? brand {
        get;
        set;
    }
    public string ? size {
        get;
        set;
    }
    public decimal price {
        get;
        set;
    }
    public virtual ICollection < Order > orders {
        get;
        set;
    }
}
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("order")]
public class Order {
    [Key, Required]
    public int id {
        get;
        set;
    }
    public int product_id {
        get;
        set;
    }
    [Required]
    public string ? name {
        get;
        set;
    }
    public string ? address {
        get;
        set;
    }
    public string ? phone {
        get;
        set;
    }
    public DateTime createdon {
        get;
        set;
    }
    public virtual Product product {
        get;
        set;
    }
}

ステップ4

DbContextクラスから継承されたEF_DataContextを作成します 

using Microsoft.EntityFrameworkCore;
public class EF_DataContext: DbContext {
    public EF_DataContext(DbContextOptions < EF_DataContext > options): base(options) {}
    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.UseSerialColumns();
    }
    public DbSet <Product> Products {
        get;
        set;
    }
    public DbSet <Order> Orders {
        get;
        set;
    }
}

ステップ5

appsetting.jsonを開きます

"ConnectionStrings": {
    "Ef_Postgres_Db": "Server=localhost;Database=shopingpostgres;Port=5432;User Id=postgres;Password=qwerty1234;"
}

ステップ6

Program.csを開く

builder.Services.AddDbContext < EF_DataContext > (o => o.UseNpgsql(builder.Configuration.GetConnectionString("Ef_Postgres_Db")));

ステップ7

2つのコマンドを実行します 

Add-Migration InitialDatabase
Update-Database

ステップ8

API通信に使用されるAPI製品モデルと注文モデルを作成します

public class Product {
    public int id {
        get;
        set;
    }
    public string ? name {
        get;
        set;
    }
    public string ? brand {
        get;
        set;
    }
    public string ? size {
        get;
        set;
    }
    public decimal price {
        get;
        set;
    }
}
public class Order {
    public int id {
        get;
        set;
    }
    public int product_id {
        get;
        set;
    }
    public string ? name {
        get;
        set;
    }
    public string ? address {
        get;
        set;
    }
    public string ? phone {
        get;
        set;
    }
    public DateTime createdon {
        get;
        set;
    }
    public virtual Product product {
        get;
        set;
    }
}

ステップ9

データベースと通信するDBhelperクラスを追加します

using ShoppingWebApi.EfCore;
namespace ShoppingWebApi.Model {
    public class DbHelper {
        private EF_DataContext _context;
        public DbHelper(EF_DataContext context) {
            _context = context;
        }
        /// <summary>
        /// GET
        /// </summary>
        /// <returns></returns>
        public List < ProductModel > GetProducts() {
            List < ProductModel > response = new List < ProductModel > ();
            var dataList = _context.Products.ToList();
            dataList.ForEach(row => response.Add(new ProductModel() {
                brand = row.brand,
                    id = row.id,
                    name = row.name,
                    price = row.price,
                    size = row.size
            }));
            return response;
        }
        public ProductModel GetProductById(int id) {
            ProductModel response = new ProductModel();
            var row = _context.Products.Where(d => d.id.Equals(id)).FirstOrDefault();
            return new ProductModel() {
                brand = row.brand,
                    id = row.id,
                    name = row.name,
                    price = row.price,
                    size = row.size
            };
        }
        /// <summary>
        /// It serves the POST/PUT/PATCH
        /// </summary>
        public void SaveOrder(OrderModel orderModel) {
            Order dbTable = new Order();
            if (orderModel.id > 0) {
                //PUT
                dbTable = _context.Orders.Where(d => d.id.Equals(orderModel.id)).FirstOrDefault();
                if (dbTable != null) {
                    dbTable.phone = orderModel.phone;
                    dbTable.address = orderModel.address;
                }
            } else {
                //POST
                dbTable.phone = orderModel.phone;
                dbTable.address = orderModel.address;
                dbTable.name = orderModel.name;
                dbTable.Product = _context.Products.Where(f => f.id.Equals(orderModel.product_id)).FirstOrDefault();
                _context.Orders.Add(dbTable);
            }
            _context.SaveChanges();
        }
        /// <summary>
        /// DELETE
        /// </summary>
        /// <param name="id"></param>
        public void DeleteOrder(int id) {
            var order = _context.Orders.Where(d => d.id.Equals(id)).FirstOrDefault();
            if (order != null) {
                _context.Orders.Remove(order);
                _context.SaveChanges();
            }
        }
    }
}

ステップ10

Apiコントローラーを作成し、ShoppingRestApiという名前を付けます

using Microsoft.AspNetCore.Mvc;
using ShoppingWebApi.EfCore;
using ShoppingWebApi.Model;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace ShoppingWebApi.Controllers {
    [ApiController]
    public class ShoppingApiController: ControllerBase {
        private readonly DbHelper _db;
        public ShoppingApiController(EF_DataContext eF_DataContext) {
            _db = new DbHelper(eF_DataContext);
        }
        // GET: api/<ShoppingApiController>
        [HttpGet]
        [Route("api/[controller]/GetProducts")]
        public IActionResult Get() {
            ResponseType type = ResponseType.Success;
            try {
                IEnumerable < ProductModel > data = _db.GetProducts();
                if (!data.Any()) {
                    type = ResponseType.NotFound;
                }
                return Ok(ResponseHandler.GetAppResponse(type, data));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
        // GET api/<ShoppingApiController>/5
        [HttpGet]
        [Route("api/[controller]/GetProductById/{id}")]
        public IActionResult Get(int id) {
            ResponseType type = ResponseType.Success;
            try {
                ProductModel data = _db.GetProductById(id);
                if (data == null) {
                    type = ResponseType.NotFound;
                }
                return Ok(ResponseHandler.GetAppResponse(type, data));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
        // POST api/<ShoppingApiController>
        [HttpPost]
        [Route("api/[controller]/SaveOrder")]
        public IActionResult Post([FromBody] OrderModel model) {
            try {
                ResponseType type = ResponseType.Success;
                _db.SaveOrder(model);
                return Ok(ResponseHandler.GetAppResponse(type, model));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
        // PUT api/<ShoppingApiController>/5
        [HttpPut]
        [Route("api/[controller]/UpdateOrder")]
        public IActionResult Put([FromBody] OrderModel model) {
            try {
                ResponseType type = ResponseType.Success;
                _db.SaveOrder(model);
                return Ok(ResponseHandler.GetAppResponse(type, model));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
        // DELETE api/<ShoppingApiController>/5
        [HttpDelete]
        [Route("api/[controller]/DeleteOrder/{id}")]
        public IActionResult Delete(int id) {
            try {
                ResponseType type = ResponseType.Success;
                _db.DeleteOrder(id);
                return Ok(ResponseHandler.GetAppResponse(type, "Delete Successfully"));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
    }
}

ステップ11

API応答を処理する応答モデルと応答ハンドラーを追加します

namespace ShoppingWebApi.Model {
    public class ApiResponse {
        public string Code {
            get;
            set;
        }
        public string Message {
            get;
            set;
        }
        public object ? ResponseData {
            get;
            set;
        }
    }
    public enum ResponseType {
        Success,
        NotFound,
        Failure
    }
}

次に、ビデオで示されているように、POSTMANを使用してAPIをテストします。

このストーリーは、もともとhttps://www.c-sharpcorner.com/article/restful-api-in-net-core-using-ef-core-and-postgres/で公開されました

#restful #api #aspdotnet #postgre #efcore 

EFコアとPostgresを使用した.NETコアのRESTfulAPI

API Restful en .NET Core usando EF Core y Postgres

REST API  es una interfaz de programación de aplicaciones que pueden utilizar varios clientes (o APLICACIONES) para comunicarse con un servidor.

Rest API es un tipo de servicio web que almacena y recupera los datos necesarios para su aplicación en un formato conveniente (por ejemplo, JSON o XML).

Proporciona una gran flexibilidad a los desarrolladores, ya que no necesita ninguna biblioteca de código dependiente para acceder a los servicios web, ya que no tiene estado.

Entre los muchos protocolos admitidos por REST, el más común es HTTP .

Cuando se envía una solicitud desde el cliente mediante HTTPRequest , se envía una respuesta correspondiente desde el servidor mediante HTTPResponse . Los formatos legibles por máquina más utilizados y admitidos para solicitudes y respuestas son JSON (Notificación de objetos Javascript) y XML (Lenguaje de marcado extensible).

REST fue creado por el informático ROY FIELDING .

Las API REST se pueden usar para realizar diferentes acciones. En función de las acciones, se debe utilizar el método pertinente. Los siguientes son los 5 métodos compatibles con REST.

  1. GET: este método se utiliza para recuperar datos de la base de datos/servidor.
  2. POST: este método se utiliza para crear un nuevo registro.
  3. PUT - Este método se utiliza para modificar/reemplazar el registro. Reemplaza todo el registro.
  4. PATCH - Este método se utiliza para modificar/actualizar el registro. Reemplaza partes del registro.
  5. ELIMINAR: este método se utiliza para eliminar el registro.

Restful Api en dotnet Core usando Ef Core y Postgres

Veamos esto con un ejemplo. Sabemos que las madres nunca descansan lo suficiente. Pero tomemos estas mamás sin descanso como ejemplo y veamos cómo usan Rest API. :)

Entre las demandas excesivas de un bebé recién nacido, el cambio de pañales ocupa el primer lugar en la tabla de clasificación.

Una madre quiere todo lo mejor para el bebé. Entonces, es obvio que la madre querría elegir el mejor pañal para su bebé. Entonces, ella va a un sitio web de compras (supongamos: flipkart) y busca pañales. Esto enviará una solicitud HTTP al servidor de flipkart para OBTENER la lista de todos los pañales. El servidor de Flipkart responde con una respuesta HTTP que será un objeto JSON (supongamos) que contiene una lista de pañales con algunos detalles básicos. El sitio web de Flipkart lee esta Respuesta, la convierte a un formato legible por humanos y la muestra en la página web para que la madre la vea.

Después, elige un pañal en particular para su bebé recién nacido y lo agrega a su lista. Esto crea una solicitud POST donde se crea un nuevo registro en la base de datos de flipkart que contiene la marca, el tamaño, la cantidad, el precio, etc. del pañal.

Su bebé sigue creciendo y pronto supera el tamaño del recién nacido. Supongamos que a la madre todavía le gusta la marca de pañales y solo quiere aumentar su tamaño, todo lo que tiene que hacer es elegir el nuevo tamaño de pañal. Cuando actualiza el tamaño del pañal del tamaño recién nacido al tamaño 1, se activa un método PATCH donde todo lo demás permanece igual y solo se cambia el tamaño del pañal.

Es muy común que la madre cambie la marca actual y decida cambiar a una alternativa. Aquí, la madre iniciará una solicitud PUT donde todos los datos que contienen la marca elegida previamente se modifican y reemplazan con los datos correspondientes a la marca recién elegida.

Finalmente, después de una serie de experimentos que involucran varios GET, POST, PUT y PATCH, es hora de que la madre enseñe al niño a ir al baño. Si logra entrenar al niño, ya no necesitará los pañales. Esto activa una solicitud DELETE .

PRERREQUISITOS

  1. estudio visual 2022
  2. .Net Core 6: ASP.NET Core es una nueva versión de ASP.NET, desarrollada por Microsoft. Es un marco de código abierto para desarrollar aplicaciones web y API y se puede ejecutar en Windows, Mac o Linux. Asp.Net Core es un marco de código abierto, multiplataforma y de alto rendimiento para crear aplicaciones modernas, basadas en la nube y conectadas a Internet.
  3. Núcleo de Entity Framework: Entity Framework (EF) Core es una versión liviana, extensible,  de código abierto  y multiplataforma de la popular tecnología de acceso a datos de Entity Framework.
  4. Base de datos de Postgres: PostgreSQL es un poderoso sistema de base de datos relacional de objetos de código abierto con más de 30 años de desarrollo activo que le ha valido una sólida reputación por su confiabilidad, robustez de funciones y rendimiento.

PASOS DE CREACIÓN DE API

Paso 1

Abra Visual Studio 2022 y cree el proyecto asp.net core webapi.

Paso 2

Instale Npgsql.EntityFrameworkCore.PostgreSQL y Microsoft.EntityFrameworkCore.Tools desde Nuget

Paso 3

Crear Product.cs y Order.cs

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("product")]
public class Product {
    [Key, Required]
    public int id {
        get;
        set;
    }
    [Required]
    public string ? name {
        get;
        set;
    }
    public string ? brand {
        get;
        set;
    }
    public string ? size {
        get;
        set;
    }
    public decimal price {
        get;
        set;
    }
    public virtual ICollection < Order > orders {
        get;
        set;
    }
}
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("order")]
public class Order {
    [Key, Required]
    public int id {
        get;
        set;
    }
    public int product_id {
        get;
        set;
    }
    [Required]
    public string ? name {
        get;
        set;
    }
    public string ? address {
        get;
        set;
    }
    public string ? phone {
        get;
        set;
    }
    public DateTime createdon {
        get;
        set;
    }
    public virtual Product product {
        get;
        set;
    }
}

Paso 4

Crear EF_DataContext heredado de la clase DbContext 

using Microsoft.EntityFrameworkCore;
public class EF_DataContext: DbContext {
    public EF_DataContext(DbContextOptions < EF_DataContext > options): base(options) {}
    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.UseSerialColumns();
    }
    public DbSet <Product> Products {
        get;
        set;
    }
    public DbSet <Order> Orders {
        get;
        set;
    }
}

Paso 5

Abrir appsetting.json

"ConnectionStrings": {
    "Ef_Postgres_Db": "Server=localhost;Database=shopingpostgres;Port=5432;User Id=postgres;Password=qwerty1234;"
}

Paso 6

Abrir programa.cs

builder.Services.AddDbContext < EF_DataContext > (o => o.UseNpgsql(builder.Configuration.GetConnectionString("Ef_Postgres_Db")));

Paso 7

Ejecuta los 2 comandos 

Add-Migration InitialDatabase
Update-Database

Paso 8

Cree los modelos de pedido y producto API que se utilizarán para la comunicación API

public class Product {
    public int id {
        get;
        set;
    }
    public string ? name {
        get;
        set;
    }
    public string ? brand {
        get;
        set;
    }
    public string ? size {
        get;
        set;
    }
    public decimal price {
        get;
        set;
    }
}
public class Order {
    public int id {
        get;
        set;
    }
    public int product_id {
        get;
        set;
    }
    public string ? name {
        get;
        set;
    }
    public string ? address {
        get;
        set;
    }
    public string ? phone {
        get;
        set;
    }
    public DateTime createdon {
        get;
        set;
    }
    public virtual Product product {
        get;
        set;
    }
}

Paso 9

Agregue la clase DBhelper que hablará con su base de datos

using ShoppingWebApi.EfCore;
namespace ShoppingWebApi.Model {
    public class DbHelper {
        private EF_DataContext _context;
        public DbHelper(EF_DataContext context) {
            _context = context;
        }
        /// <summary>
        /// GET
        /// </summary>
        /// <returns></returns>
        public List < ProductModel > GetProducts() {
            List < ProductModel > response = new List < ProductModel > ();
            var dataList = _context.Products.ToList();
            dataList.ForEach(row => response.Add(new ProductModel() {
                brand = row.brand,
                    id = row.id,
                    name = row.name,
                    price = row.price,
                    size = row.size
            }));
            return response;
        }
        public ProductModel GetProductById(int id) {
            ProductModel response = new ProductModel();
            var row = _context.Products.Where(d => d.id.Equals(id)).FirstOrDefault();
            return new ProductModel() {
                brand = row.brand,
                    id = row.id,
                    name = row.name,
                    price = row.price,
                    size = row.size
            };
        }
        /// <summary>
        /// It serves the POST/PUT/PATCH
        /// </summary>
        public void SaveOrder(OrderModel orderModel) {
            Order dbTable = new Order();
            if (orderModel.id > 0) {
                //PUT
                dbTable = _context.Orders.Where(d => d.id.Equals(orderModel.id)).FirstOrDefault();
                if (dbTable != null) {
                    dbTable.phone = orderModel.phone;
                    dbTable.address = orderModel.address;
                }
            } else {
                //POST
                dbTable.phone = orderModel.phone;
                dbTable.address = orderModel.address;
                dbTable.name = orderModel.name;
                dbTable.Product = _context.Products.Where(f => f.id.Equals(orderModel.product_id)).FirstOrDefault();
                _context.Orders.Add(dbTable);
            }
            _context.SaveChanges();
        }
        /// <summary>
        /// DELETE
        /// </summary>
        /// <param name="id"></param>
        public void DeleteOrder(int id) {
            var order = _context.Orders.Where(d => d.id.Equals(id)).FirstOrDefault();
            if (order != null) {
                _context.Orders.Remove(order);
                _context.SaveChanges();
            }
        }
    }
}

Paso 10

Cree su controlador Api, asígnele el nombre ShoppingRestApi

using Microsoft.AspNetCore.Mvc;
using ShoppingWebApi.EfCore;
using ShoppingWebApi.Model;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace ShoppingWebApi.Controllers {
    [ApiController]
    public class ShoppingApiController: ControllerBase {
        private readonly DbHelper _db;
        public ShoppingApiController(EF_DataContext eF_DataContext) {
            _db = new DbHelper(eF_DataContext);
        }
        // GET: api/<ShoppingApiController>
        [HttpGet]
        [Route("api/[controller]/GetProducts")]
        public IActionResult Get() {
            ResponseType type = ResponseType.Success;
            try {
                IEnumerable < ProductModel > data = _db.GetProducts();
                if (!data.Any()) {
                    type = ResponseType.NotFound;
                }
                return Ok(ResponseHandler.GetAppResponse(type, data));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
        // GET api/<ShoppingApiController>/5
        [HttpGet]
        [Route("api/[controller]/GetProductById/{id}")]
        public IActionResult Get(int id) {
            ResponseType type = ResponseType.Success;
            try {
                ProductModel data = _db.GetProductById(id);
                if (data == null) {
                    type = ResponseType.NotFound;
                }
                return Ok(ResponseHandler.GetAppResponse(type, data));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
        // POST api/<ShoppingApiController>
        [HttpPost]
        [Route("api/[controller]/SaveOrder")]
        public IActionResult Post([FromBody] OrderModel model) {
            try {
                ResponseType type = ResponseType.Success;
                _db.SaveOrder(model);
                return Ok(ResponseHandler.GetAppResponse(type, model));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
        // PUT api/<ShoppingApiController>/5
        [HttpPut]
        [Route("api/[controller]/UpdateOrder")]
        public IActionResult Put([FromBody] OrderModel model) {
            try {
                ResponseType type = ResponseType.Success;
                _db.SaveOrder(model);
                return Ok(ResponseHandler.GetAppResponse(type, model));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
        // DELETE api/<ShoppingApiController>/5
        [HttpDelete]
        [Route("api/[controller]/DeleteOrder/{id}")]
        public IActionResult Delete(int id) {
            try {
                ResponseType type = ResponseType.Success;
                _db.DeleteOrder(id);
                return Ok(ResponseHandler.GetAppResponse(type, "Delete Successfully"));
            } catch (Exception ex) {
                return BadRequest(ResponseHandler.GetExceptionResponse(ex));
            }
        }
    }
}

Paso 11

Agregue el modelo de respuesta y el controlador de respuesta que manejará sus respuestas API

namespace ShoppingWebApi.Model {
    public class ApiResponse {
        public string Code {
            get;
            set;
        }
        public string Message {
            get;
            set;
        }
        public object ? ResponseData {
            get;
            set;
        }
    }
    public enum ResponseType {
        Success,
        NotFound,
        Failure
    }
}

Ahora pruebe las API usando POSTMAN como se muestra en el video.

Esta historia se publicó originalmente en https://www.c-sharpcorner.com/article/restful-api-in-net-core-using-ef-core-and-postgres/

#restful #api #aspdotnet #postgre #efcore 

API Restful en .NET Core usando EF Core y Postgres

Ozzo-routing: an Extremely Fast Go (golang) HTTP Router

ozzo-routing

You may consider using go-rest-api to jumpstart your new RESTful applications with ozzo-routing.

Description

ozzo-routing is a Go package that provides high performance and powerful HTTP routing capabilities for Web applications. It has the following features:

  • middleware pipeline architecture, similar to that of the Express framework.
  • extremely fast request routing with zero dynamic memory allocation (the performance is comparable to that of httprouter and gin, see the performance comparison below)
  • modular code organization through route grouping
  • flexible URL path matching, supporting URL parameters and regular expressions
  • URL creation according to the predefined routes
  • compatible with http.Handler and http.HandlerFunc
  • ready-to-use handlers sufficient for building RESTful APIs
  • graceful shutdown

If you are using fasthttp, you may use a similar routing package fasthttp-routing which is adapted from ozzo-routing.

Requirements

Go 1.13 or above.

Installation

In your Go project using go mod, run the following command to install the package:

go get github.com/go-ozzo/ozzo-routing/v2

Getting Started

For a complete RESTful application boilerplate based on ozzo-routing, please refer to the golang-restful-starter-kit. Below we describe how to create a simple REST API using ozzo-routing.

Create a server.go file with the following content:

package main

import (
    "log"
    "net/http"
    "github.com/go-ozzo/ozzo-routing/v2"
    "github.com/go-ozzo/ozzo-routing/v2/access"
    "github.com/go-ozzo/ozzo-routing/v2/slash"
    "github.com/go-ozzo/ozzo-routing/v2/content"
    "github.com/go-ozzo/ozzo-routing/v2/fault"
    "github.com/go-ozzo/ozzo-routing/v2/file"
)

func main() {
    router := routing.New()

    router.Use(
        // all these handlers are shared by every route
        access.Logger(log.Printf),
        slash.Remover(http.StatusMovedPermanently),
        fault.Recovery(log.Printf),
    )

    // serve RESTful APIs
    api := router.Group("/api")
    api.Use(
        // these handlers are shared by the routes in the api group only
        content.TypeNegotiator(content.JSON, content.XML),
    )
    api.Get("/users", func(c *routing.Context) error {
        return c.Write("user list")
    })
    api.Post("/users", func(c *routing.Context) error {
        return c.Write("create a new user")
    })
    api.Put(`/users/<id:\d+>`, func(c *routing.Context) error {
        return c.Write("update user " + c.Param("id"))
    })

    // serve index file
    router.Get("/", file.Content("ui/index.html"))
    // serve files under the "ui" subdirectory
    router.Get("/*", file.Server(file.PathMap{
        "/": "/ui/",
    }))

    http.Handle("/", router)
    http.ListenAndServe(":8080", nil)
}

Create an HTML file ui/index.html with any content.

Now run the following command to start the Web server:

go run server.go

You should be able to access URLs such as http://localhost:8080, http://localhost:8080/api/users.

Routes

ozzo-routing works by building a routing table in a router and then dispatching HTTP requests to the matching handlers found in the routing table. An intuitive illustration of a routing table is as follows:

RoutesHandlers
GET /usersm1, m2, h1, ...
POST /usersm1, m2, h2, ...
PUT /users/<id>m1, m2, h3, ...
DELETE /users/<id>m1, m2, h4, ...

For an incoming request GET /users, the first route would match and the handlers m1, m2, and h1 would be executed. If the request is PUT /users/123, the third route would match and the corresponding handlers would be executed. Note that the token <id> can match any number of non-slash characters and the matching part can be accessed as a path parameter value in the handlers.

If an incoming request matches multiple routes in the table, the route added first to the table will take precedence. All other matching routes will be ignored.

The actual implementation of the routing table uses a variant of the radix tree data structure, which makes the routing process as fast as working with a hash table, thanks to the inspiration from httprouter.

To add a new route and its handlers to the routing table, call the To method like the following:

router := routing.New()
router.To("GET", "/users", m1, m2, h1)
router.To("POST", "/users", m1, m2, h2)

You can also use shortcut methods, such as Get, Post, Put, etc., which are named after the HTTP method names:

router.Get("/users", m1, m2, h1)
router.Post("/users", m1, m2, h2)

If you have multiple routes with the same URL path but different HTTP methods, like the above example, you can chain them together as follows,

router.Get("/users", m1, m2, h1).Post(m1, m2, h2)

If you want to use the same set of handlers to handle the same URL path but different HTTP methods, you can take the following shortcut:

router.To("GET,POST", "/users", m1, m2, h)

A route may contain parameter tokens which are in the format of <name:pattern>, where name stands for the parameter name, and pattern is a regular expression which the parameter value should match. A token <name> is equivalent to <name:[^/]*>, i.e., it matches any number of non-slash characters. At the end of a route, an asterisk character can be used to match any number of arbitrary characters. Below are some examples:

  • /users/<username>: matches /users/admin
  • /users/accnt-<id:\d+>: matches /users/accnt-123, but not /users/accnt-admin
  • /users/<username>/*: matches /users/admin/profile/address

When a URL path matches a route, the matching parameters on the URL path can be accessed via Context.Param():

router := routing.New()

router.Get("/users/<username>", func (c *routing.Context) error {
    fmt.Fprintf(c.Response, "Name: %v", c.Param("username"))
    return nil
})

Route Groups

Route group is a way of grouping together the routes which have the same route prefix. The routes in a group also share the same handlers that are registered with the group via its Use method. For example,

router := routing.New()
api := router.Group("/api")
api.Use(m1, m2)
api.Get("/users", h1).Post(h2)
api.Put("/users/<id>", h3).Delete(h4)

The above /api route group establishes the following routing table:

RoutesHandlers
GET /api/usersm1, m2, h1, ...
POST /api/usersm1, m2, h2, ...
PUT /api/users/<id>m1, m2, h3, ...
DELETE /api/users/<id>m1, m2, h4, ...

As you can see, all these routes have the same route prefix /api and the handlers m1 and m2. In other similar routing frameworks, the handlers registered with a route group are also called middlewares.

Route groups can be nested. That is, a route group can create a child group by calling the Group() method. The router serves as the top level route group. A child group inherits the handlers registered with its parent group. For example,

router := routing.New()
router.Use(m1)

api := router.Group("/api")
api.Use(m2)

users := api.Group("/users")
users.Use(m3)
users.Put("/<id>", h1)

Because the router serves as the parent of the api group which is the parent of the users group, the PUT /api/users/<id> route is associated with the handlers m1, m2, m3, and h1.

Router

Router manages the routing table and dispatches incoming requests to appropriate handlers. A router instance is created by calling the routing.New() method.

Because Router implements the http.Handler interface, it can be readily used to serve subtrees on existing Go servers. For example,

router := routing.New()
http.Handle("/", router)
http.ListenAndServe(":8080", nil)

Handlers

A handler is a function with the signature func(*routing.Context) error. A handler is executed by the router if the incoming request URL path matches the route that the handler is associated with. Through the routing.Context parameter, you can access the request information in handlers.

A route may be associated with multiple handlers. These handlers will be executed in the order that they are registered to the route. The execution sequence can be terminated in the middle using one of the following two methods:

  • A handler returns an error: the router will skip the rest of the handlers and handle the returned error.
  • A handler calls Context.Abort(): the router will simply skip the rest of the handlers. There is no error to be handled.

A handler can call Context.Next() to explicitly execute the rest of the unexecuted handlers and take actions after they finish execution. For example, a response compression handler may start the output buffer, call Context.Next(), and then compress and send the output to response.

Context

For each incoming request, a routing.Context object is populated with the request information and passed through the handlers that need to handle the request. Handlers can get the request information via Context.Request and send a response back via Context.Response. The Context.Param() method allows handlers to access the URL path parameters that match the current route.

Using Context.Get() and Context.Set(), handlers can share data between each other. For example, an authentication handler can store the authenticated user identity by calling Context.Set(), and other handlers can retrieve back the identity information by calling Context.Get().

Reading Request Data

Context provides a few shortcut methods to read query parameters. The Context.Query() method returns the named URL query parameter value; the Context.PostForm() method returns the named parameter value in the POST or PUT body parameters; and the Context.Form() method returns the value from either POST/PUT or URL query parameters.

The Context.Read() method supports reading data from the request body and populating it into an object. The method will check the Content-Type HTTP header and parse the body data as the corresponding format. For example, if Content-Type is application/json, the request body will be parsed as JSON data. The public fields in the object being populated will receive the parsed data if the data contains the same named fields. For example,

func foo(c *routing.Context) error {
    data := &struct{
        A string
        B bool
    }{}

    // assume the body data is: {"A":"abc", "B":true}
    // data will be populated as: {A: "abc", B: true}
    if err := c.Read(&data); err != nil {
        return err
    }
}

By default, Context supports reading data that are in JSON, XML, form, and multipart-form data. You may modify routing.DataReaders to add support for other data formats.

Note that when the data is read as form data, you may use struct tag named form to customize the name of the corresponding field in the form data. The form data reader also supports populating data into embedded objects which are either named or anonymous.

Writing Response Data

The Context.Write() method can be used to write data of arbitrary type to the response. By default, if the data being written is neither a string nor a byte array, the method will will call fmt.Fprint() to write the data into the response.

You can call Context.SetWriter() to replace the default data writer with a customized one. For example, the content.TypeNegotiator will negotiate the content response type and set the data writer with an appropriate one.

Error Handling

A handler may return an error indicating some erroneous condition. Sometimes, a handler or the code it calls may cause a panic. Both should be handled properly to ensure best user experience. It is recommended that you use the fault.Recover handler or a similar error handler to handle these errors.

If an error is not handled by any handler, the router will handle it by calling its handleError() method which simply sets an appropriate HTTP status code and writes the error message to the response.

When an incoming request has no matching route, the router will call the handlers registered via the Router.NotFound() method. All the handlers registered via Router.Use() will also be called in advance. By default, the following two handlers are registered with Router.NotFound():

  • routing.MethodNotAllowedHandler: a handler that sends an Allow HTTP header indicating the allowed HTTP methods for a requested URL
  • routing.NotFoundHandler: a handler triggering 404 HTTP error

Serving Static Files

Static files can be served with the help of file.Server and file.Content handlers. The former serves files under the specified directories, while the latter serves the content of a single file. For example,

import (
    "github.com/go-ozzo/ozzo-routing/v2"
    "github.com/go-ozzo/ozzo-routing/v2/file"
)

router := routing.NewRouter()

// serve index file
router.Get("/", file.Content("ui/index.html"))
// serve files under the "ui" subdirectory
router.Get("/*", file.Server(file.PathMap{
    "/": "/ui/",
}))

Handlers

ozzo-routing comes with a few commonly used handlers in its subpackages:

Handler nameDescription
access.Loggerrecords an entry for every incoming request
auth.Basicprovides authentication via HTTP Basic
auth.Bearerprovides authentication via HTTP Bearer
auth.Queryprovides authentication via token-based query parameter
auth.JWTprovides JWT-based authentication
content.TypeNegotiatorsupports content negotiation by response types
content.LanguageNegotiatorsupports content negotiation by accepted languages
cors.Handlerimplements the CORS (Cross Origin Resource Sharing) specification from the W3C
fault.Recoveryrecovers from panics and handles errors returned by handlers
fault.PanicHandlerrecovers from panics happened in the handlers
fault.ErrorHandlerhandles errors returned by handlers by writing them in an appropriate format to the response
file.Serverserves the files under the specified folder as response content
file.Contentserves the content of the specified file as the response
slash.Removerremoves the trailing slashes from the request URL and redirects to the proper URL

The following code shows how these handlers may be used:

import (
    "log"
    "net/http"
    "github.com/go-ozzo/ozzo-routing/v2"
    "github.com/go-ozzo/ozzo-routing/v2/access"
    "github.com/go-ozzo/ozzo-routing/v2/slash"
    "github.com/go-ozzo/ozzo-routing/v2/fault"
)

router := routing.New()

router.Use(
    access.Logger(log.Printf),
    slash.Remover(http.StatusMovedPermanently),
    fault.Recovery(log.Printf),
)

...

Third-party Handlers

The following third-party handlers are specifically designed for ozzo-routing:

Handler nameDescription
jwt.JWTsupports JWT Authorization

ozzo-routing also provides adapters to support using third-party http.HandlerFunc or http.Handler handlers. For example,

router := routing.New()

// using http.HandlerFunc
router.Use(routing.HTTPHandlerFunc(http.NotFound))

// using http.Handler
router.Use(routing.HTTPHandler(http.NotFoundHandler))

3rd-Party Extensions and Code Examples

Benchmarks

Last updated on Jan 6, 2017

Ozzo-routing is very fast, thanks to the radix tree data structure and the usage of sync.Pool (the idea was originally from HttpRouter and Gin). The following table (by running go-http-routing-benchmark) shows how ozzo-routing compares with Gin, HttpRouter, and Martini in performance.

BenchmarkOzzo_GithubAll                    50000             37989 ns/op               0 B/op          0 allocs/op
BenchmarkEcho_GithubAll                    20000             91003 ns/op            6496 B/op        203 allocs/op
BenchmarkGin_GithubAll                     50000             26717 ns/op               0 B/op          0 allocs/op
BenchmarkHttpRouter_GithubAll              50000             36052 ns/op           13792 B/op        167 allocs/op
BenchmarkMartini_GithubAll                   300           4162283 ns/op          228216 B/op       2483 allocs/op

BenchmarkOzzo_GPlusAll                   1000000              1732 ns/op               0 B/op          0 allocs/op
BenchmarkEcho_GPlusAll                    300000              4523 ns/op             416 B/op         13 allocs/op
BenchmarkGin_GPlusAll                    1000000              1171 ns/op               0 B/op          0 allocs/op
BenchmarkHttpRouter_GPlusAll             1000000              1533 ns/op             640 B/op         11 allocs/op
BenchmarkMartini_GPlusAll                  20000             75634 ns/op           14448 B/op        165 allocs/op

BenchmarkOzzo_ParseAll                    500000              3318 ns/op               0 B/op          0 allocs/op
BenchmarkEcho_ParseAll                    200000              7336 ns/op             832 B/op         26 allocs/op
BenchmarkGin_ParseAll                    1000000              2075 ns/op               0 B/op          0 allocs/op
BenchmarkHttpRouter_ParseAll             1000000              2034 ns/op             640 B/op         16 allocs/op
BenchmarkMartini_ParseAll                  10000            122002 ns/op           25600 B/op        276 allocs/op

Credits

ozzo-routing has referenced many popular routing frameworks, including Express, Martini, httprouter, and gin.

Author: Go-ozzo
Source Code: https://github.com/go-ozzo/ozzo-routing 
License: MIT license

#go #golang #http #restful 

Ozzo-routing: an Extremely Fast Go (golang) HTTP Router

Xujiajun/gorouter: A Simple and Fast HTTP Router for Go

gorouter

xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework.

Motivation

I wanted a simple and fast HTTP GO router, which supports regexp. I prefer to support regexp is because otherwise it will need the logic to check the URL parameter type, thus increasing the program complexity. So I did some searching on Github and found the wonderful julienschmidt/httprouter: it is very fast,unfortunately it does not support regexp. Later I found out about gorilla/mux: it is powerful as well,but a written benchmark shows me that it is somewhat slow. So I tried to develop a new router which both supports regexp and should be fast. Finally I did it and named xujiajun/gorouter. By the way, this is my first GO open source project. It may be the fastest GO HTTP router which supports regexp, and regarding its performance please refer to my latest Benchmarks.

Features

Requirements

  • golang 1.8+

Installation

go get -u github.com/xujiajun/gorouter

Usage

Static routes

package main

import (
    "log"
    "net/http"

    "github.com/xujiajun/gorouter"
)

func main() {
    mux := gorouter.New()
    mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hello world"))
    })
    log.Fatal(http.ListenAndServe(":8181", mux))
}

URL Parameters

package main

import (
    "log"
    "net/http"

    "github.com/xujiajun/gorouter"
)

func main() {
    mux := gorouter.New()
    //url parameters match
    mux.GET("/user/:id", func(w http.ResponseWriter, r *http.Request) {
        //get one URL parameter
        id := gorouter.GetParam(r, "id")
        //get all URL parameters
        //id := gorouter.GetAllParams(r)
        //fmt.Println(id)
        w.Write([]byte("match user/:id ! get id:" + id))
    })

    log.Fatal(http.ListenAndServe(":8181", mux))
}

Regex Parameters

package main

import (
    "log"
    "net/http"

    "github.com/xujiajun/gorouter"
)

func main() {
    mux := gorouter.New()
    //url regex match
    mux.GET("/user/{id:[0-9]+}", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("match user/{id:[0-9]+} !"))
    })

    log.Fatal(http.ListenAndServe(":8181", mux))
}

Routes Groups

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/xujiajun/gorouter"
)

func usersHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "/api/users")
}

func main() {
    mux := gorouter.New()
    mux.Group("/api").GET("/users", usersHandler)

    log.Fatal(http.ListenAndServe(":8181", mux))
}

Reverse Routing

package main

import (
    "fmt"
    "net/http"

    "github.com/xujiajun/gorouter"
)

func main() {
    mux := gorouter.New()

    routeName1 := "user_event"
    mux.GETAndName("/users/:user/events", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("/users/:user/events"))
    }, routeName1)

    routeName2 := "repos_owner"
    mux.GETAndName("/repos/{owner:\\w+}/{repo:\\w+}", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("/repos/{owner:\\w+}/{repo:\\w+}"))
    }, routeName2)

    params := make(map[string]string)
    params["user"] = "xujiajun"
    fmt.Println(mux.Generate(http.MethodGet, routeName1, params)) // /users/xujiajun/events <nil>

    params = make(map[string]string)
    params["owner"] = "xujiajun"
    params["repo"] = "xujiajun_repo"
    fmt.Println(mux.Generate(http.MethodGet, routeName2, params)) // /repos/xujiajun/xujiajun_repo <nil>
}

Custom NotFoundHandler

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/xujiajun/gorouter"
)

func notFoundFunc(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusNotFound)
    fmt.Fprint(w, "404 page !!!")
}

func main() {
    mux := gorouter.New()
    mux.NotFoundFunc(notFoundFunc)
    mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hello world"))
    })

    log.Fatal(http.ListenAndServe(":8181", mux))
}

Custom PanicHandler

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/xujiajun/gorouter"
)

func main() {
    mux := gorouter.New()
    mux.PanicHandler = func(w http.ResponseWriter, req *http.Request, err interface{}) {
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Println("err from recover is :", err)
        fmt.Fprint(w, "received a panic")
    }
    mux.GET("/panic", func(w http.ResponseWriter, r *http.Request) {
        panic("panic")
    })

    log.Fatal(http.ListenAndServe(":8181", mux))
}

Middlewares Chain

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/xujiajun/gorouter"
)

type statusRecorder struct {
    http.ResponseWriter
    status int
}

func (rec *statusRecorder) WriteHeader(code int) {
    rec.status = code
    rec.ResponseWriter.WriteHeader(code)
}

//https://upgear.io/blog/golang-tip-wrapping-http-response-writer-for-middleware/
func withStatusRecord(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        rec := statusRecorder{w, http.StatusOK}
        next.ServeHTTP(&rec, r)
        log.Printf("response status: %v\n", rec.status)
    }
}

func notFoundFunc(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusNotFound)
    fmt.Fprint(w, "Not found page !")
}

func withLogging(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Printf("Logged connection from %s", r.RemoteAddr)
        next.ServeHTTP(w, r)
    }
}

func withTracing(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Printf("Tracing request for %s", r.RequestURI)
        next.ServeHTTP(w, r)
    }
}

func main() {
    mux := gorouter.New()
    mux.NotFoundFunc(notFoundFunc)
    mux.Use(withLogging, withTracing, withStatusRecord)
    mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hello world"))
    })

    log.Fatal(http.ListenAndServe(":8181", mux))
}

Serve static files

package main

import (
    "log"
    "net/http"
    "os"
    
    "github.com/xujiajun/gorouter"
)

//ServeFiles serve static resources
func ServeFiles(w http.ResponseWriter, r *http.Request) {
    wd, err := os.Getwd()
    if err != nil {
        log.Fatal(err)
    }

    dir := wd + "/examples/serveStaticFiles/files"
    http.StripPrefix("/files/", http.FileServer(http.Dir(dir))).ServeHTTP(w, r)
}

func main() {
    mux := gorouter.New()
    mux.GET("/hi", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hi"))
    })
    //defined prefix
    mux2 := mux.Group("/files")
    //http://127.0.0.1:8181/files/demo.txt
    //will match
    mux2.GET("/{filename:[0-9a-zA-Z_.]+}", func(w http.ResponseWriter, r *http.Request) {
        ServeFiles(w, r)
    })

    //http://127.0.0.1:8181/files/a/demo2.txt
    //http://127.0.0.1:8181/files/a/demo.txt
    //will match
    mux2.GET("/{fileDir:[0-9a-zA-Z_.]+}/{filename:[0-9a-zA-Z_.]+}", func(w http.ResponseWriter, r *http.Request) {
        ServeFiles(w, r)
    })

    log.Fatal(http.ListenAndServe(":8181", mux))
}

Detail see serveStaticFiles example

Pattern Rule

The syntax here is modeled after julienschmidt/httprouter and gorilla/mux

SyntaxDescriptionExample
:namenamed parameter/user/:name
{name:regexp}named with regexp parameter/user/{name:[0-9a-zA-Z]+}
:idnamed with regexp parameter/user/:id

And :id is short for {id:[0-9]+}, :name are short for {name:[0-9a-zA-Z_]+}

if use default regex checks unless you know what you're doing

Benchmarks

the benchmarks code for gorouter be found in the gorouter-bench repository.

go test -bench=.

Benchmark System:

  • Go Version : go1.11.2 darwin/amd64
  • OS: Mac OS X 10.13.6
  • Architecture: x86_64
  • 16 GB 2133 MHz LPDDR3
  • CPU: 3.1 GHz Intel Core i7

Tested routers:

Thanks the author of httprouter: @julienschmidt give me advise about benchmark issues/24

Result:

Given some routing matching syntax differences, divide GithubAPI into two groups:

Using GithubAPI Result:

BenchmarkBeegoMuxRouterWithGithubAPI-8          10000        142398 ns/op      134752 B/op        1038 allocs/op
BenchmarkBoneRouterWithGithubAPI-8               1000       2104486 ns/op      720160 B/op        8620 allocs/op
BenchmarkTrieMuxRouterWithGithubAPI-8           20000         80845 ns/op       65856 B/op         537 allocs/op
BenchmarkHttpRouterWithGithubAPI-8              50000         30169 ns/op       13792 B/op         167 allocs/op
BenchmarkGoRouter1WithGithubAPI-8               30000         57793 ns/op       13832 B/op         406 allocs/op

Using GithubAPI2 Result:

BenchmarkGoRouter2WithGithubAPI2-8              30000         57613 ns/op       13832 B/op         406 allocs/op
BenchmarkChiRouterWithGithubAPI2-8              10000        143224 ns/op      104436 B/op        1110 allocs/op
BenchmarkMuxRouterWithGithubAPI2-8                300       4450731 ns/op       61463 B/op         995 allocs/op

All togther Result:

➜  gorouter git:(master) go test -bench=.
GithubAPI Routes: 203
GithubAPI2 Routes: 203
   BeegoMuxRouter: 111072 Bytes
   BoneRouter: 100992 Bytes
   ChiRouter: 71512 Bytes
   HttpRouter: 37016 Bytes
   trie-mux: 131128 Bytes
   MuxRouter: 1378496 Bytes
   GoRouter1: 83824 Bytes
   GoRouter2: 85584 Bytes
goos: darwin
goarch: amd64
pkg: github.com/xujiajun/gorouter
BenchmarkBeegoMuxRouterWithGithubAPI-8          10000        142398 ns/op      134752 B/op        1038 allocs/op
BenchmarkBoneRouterWithGithubAPI-8               1000       2104486 ns/op      720160 B/op        8620 allocs/op
BenchmarkTrieMuxRouterWithGithubAPI-8           20000         80845 ns/op       65856 B/op         537 allocs/op
BenchmarkHttpRouterWithGithubAPI-8              50000         30169 ns/op       13792 B/op         167 allocs/op
BenchmarkGoRouter1WithGithubAPI-8               30000         57793 ns/op       13832 B/op         406 allocs/op
BenchmarkGoRouter2WithGithubAPI2-8              30000         57613 ns/op       13832 B/op         406 allocs/op
BenchmarkChiRouterWithGithubAPI2-8              10000        143224 ns/op      104436 B/op        1110 allocs/op
BenchmarkMuxRouterWithGithubAPI2-8                300       4450731 ns/op       61463 B/op         995 allocs/op
PASS
ok      github.com/xujiajun/gorouter    15.918s

Conclusions:

Performance (xujiajun/gorouter,julienschmidt/httprouter and teambition/trie-mux are fast)

Memory Consumption (xujiajun/gorouter and julienschmidt/httprouter are fewer)

Features (julienschmidt/httprouter not supports regexp,but others support it)

if you want a high performance router which supports regexp, maybe xujiajun/gorouter is good choice.

if you want a high performance router which not supports regexp, maybe julienschmidt/httprouter is good choice.

In the end, as julienschmidt said performance can not be the (only) criterion for choosing a router. Play around a bit with some of the routers, and choose the one you like best.

Contributing

If you'd like to help out with the project. You can put up a Pull Request. Thanks to all contributors.

Acknowledgements

This package is inspired by the following:

Author: Xujiajun
Source Code: https://github.com/xujiajun/gorouter 
License: MIT license

#go #golang #restful #api 

Xujiajun/gorouter: A Simple and Fast HTTP Router for Go
Waylon  Bruen

Waylon Bruen

1653482116

Tome: Package Tome Was Designed To Paginate Simple RESTful APIs

Package tome was designed to paginate simple RESTful APIs.

Installation

go get -u github.com/cyruzin/tome

Usage

To get started, import the tome package and initiate the pagination:

import "github.com/cyruzin/tome"

// Post type is a struct for a single post.
type Post struct {
    Title string `json:"title"`
    Body  string `json:"body"`
}

// Posts type is a struct for multiple posts.
type Posts []*Post

// Result type is a struct of posts with pagination.
type Result struct {
    Data *Posts `json:"data"`
    *tome.Chapter
}

// GetPosts gets the latest 10 posts with pagination.
func GetPosts(w http.ResponseWriter, r *http.Request) {
    // Creating a tome chapter with links.
    chapter := &tome.Chapter{
        // Setting base URL.
        BaseURL: "http://yourapi.com/v1/posts",
        // Enabling link results.
        Links: true,
        // Page that you captured in params inside you handler.
        NewPage: 2,
        // Total of pages, this usually comes from a SQL query total rows result.
        TotalResults: model.GetPostsTotalResults(),
    }

    // Paginating the results.
    if err := chapter.Paginate(); err != nil { 
        w.WriteHeader(http.StatusUnprocessableEntity)  // Setting status 422.
            json.NewEncoder(w).Encode(err) // Returning JSON with an error.
        return
    }

    // Here you pass the offset and limit.
    database, err := model.GetPosts(chapter.Offset, chapter.Limit)
    if err != nil {
        w.WriteHeader(http.StatusUnprocessableEntity)  // Setting status 422.
            json.NewEncoder(w).Encode(err) // Returning JSON with an error.
        return
    }

    // Mocking results with pagination.
    res := &Result{Data: database, Chapter: chapter}
    
    w.WriteHeader(http.StatusOK)  // Setting status 200.
    json.NewEncoder(w).Encode(res) // Returning success JSON.
}

Output:

{
 "data": [
  {
   "title": "What is Lorem Ipsum?",
   "body": "Lorem Ipsum is simply dummy text of the printing and..."
  },
  {
   "title": "Why do we use it?",
   "body": "It is a long established fact that a reader will be..."
  }
 ],
 "base_url": "http://yourapi.com/v1/posts",
 "next_url": "http://yourapi.com/v1/posts?page=3",
 "prev_url": "http://yourapi.com/v1/posts?page=1",
 "per_page": 10,
 "current_page": 2,
 "last_page": 30,
 "total_results": 300
}

Performance

Without links:

Iterationsns/opB/opallocs/op
2000000007.8000

With links:

Iterationsns/opB/opallocs/op
10000000133962

Author: Cyruzin
Source Code: https://github.com/cyruzin/tome 
License: MIT license

#go #golang #api #restful 

Tome: Package Tome Was Designed To Paginate Simple RESTful APIs

Awesome Python: Libraries for Building RESTful APIs

RESTful API

Libraries for building RESTful APIs.

  • Django
  • Flask
    • eve - REST API framework powered by Flask, MongoDB and good intentions.
    • flask-api - Browsable Web APIs for Flask.
    • flask-restful - Quickly building REST APIs for Flask.
  • Pyramid
    • cornice - A RESTful framework for Pyramid.
  • Framework agnostic
    • apistar - A smart Web API framework, designed for Python 3.
    • falcon - A high-performance framework for building cloud APIs and web app backends.
    • fastapi - A modern, fast, web framework for building APIs with Python 3.6+ based on standard Python type hints.
    • hug - A Python 3 framework for cleanly exposing APIs.
    • sandman2 - Automated REST APIs for existing database-driven systems.
    • sanic - A Python 3.6+ web server and web framework that's written to go fast.

Author: vinta
Source Code: https://github.com/vinta/awesome-python
License: View license

#python #restful #api 

Awesome Python: Libraries for Building RESTful APIs

Sandman2: Automatically Generate A RESTful API Service For Database

sandman2

sandman2 documentation

[ ~ Dependencies scanned by PyUp.io ~ ]

sandman2 automagically generates a RESTful API service from your existing database, without requiring you to write a line of code. Simply point sandman2 to your database, add salt for seasoning, and voila!, a fully RESTful API service with hypermedia support starts running, ready to accept HTTP requests.

This is a big deal. It means every single database you interact with, from the SQLite database that houses your web browser's data up to your production PostgreSQL server can be endowed with a REST API and accessed programmatically, using any number of HTTP client libraries available in every language. sandman2 frees your data.

For developers:

Imagine you're working for AnonymousCorp and need to access Group Y's data, which is presented to you through some horrible API or GUI. Wouldn't it be nice if you could just interact with that database through a REST API?

More than that, imagine if you could interact with the database through a REST API and no one had to write any code. Not you. Not Group Y. No one. That means no boilerplate ORM code, no database connection logic. Nothing. sandman2 can be run as a command-line tool (sandman2ctl) that just takes your database information as parameters and connects to it, introspects the schema, generates a RESTful API, and starts the server.

What Happened to Sandman (1)?

sandman, the precursor to sandman2, is no longer being maintained. sandman had almost identical functionality but had an architecture that reflected the capabilities of the underlying ORM, SQLAlchemy. As of the 0.9 release, SQLAlchemy introduced the automap construct. This fundamentally changed the way that sandman could interact with the underlying database in a way that greatly simplified things. All that was needed was the actual effort to rewrite sandman from scratch...

sandman2 has since surpassed the functionality of the original sandman and the latter should be considered deprecated/obsolete.

Quickstart

Install sandman2 using pip: $ pip install sandman2. This provides the script sandman2ctl, which just takes the database URI string, described here. For example, to connect to a SQLite database in the same directory you're running the script, you would run:

$ sandman2ctl sqlite+pysqlite:///database_file_name

To connect to a PostgreSQL database, make sure you install a driver like psycopg2 using pip, then use the following connection string:

$ sandman2ctl postgresql+psycopg2://scott:tiger@localhost/mydatabase

Again, see the SQLAlchemy documentation for a more comprehensive discussion of connection strings.

Supported Databases

sandman2 supports all databases that the underlying ORM, SQLAlchemy, supports. Presently, that includes:

  • MySQL
  • PostgreSQL
  • Oracle
  • Microsoft SQL Server
  • SQLite
  • Sybase
  • Drizzle
  • Firebird

Third-party packages extend support to:

  • IBM DB2
  • Amazon Redshift
  • SQL Anywhere
  • MonetDB

Admin Interface

One of the best things about the original sandman was the Admin Interface. Not only does sandman2 include the Admin Interface, but it modernizes it as well. The layout has been greatly improved, especially when dealing with larger numbers of tables. All of the original functionality of the Admin Interface remains unchanged.

Here's a shot of the new look:

admin interface awesomesauce screenshot

Customizing

If sandman2ctl doesn't give you fine-grained enough control over your REST endpoints, or you'd like to restrict the set of tables made available via sandman2ctl, you can easily integrate sandman2 into your application. See the documentation for more info.

Running sandman2 under Docker

sandman2 has an official docker image at Docker Hub. Simply docker pull jeffknupp/sandman2 to get the latest version. It supports the most popular database engines, but not all that sandman2 currently natively supports. If you'd like to see support for your RDBMS, either add a pull request on this repo (if possible) or create a new issue with the details of your database's Python driver.

Example

Here's how one would run sandman2 to connect to a PostgreSQL database running on one's host machine (i.e. not a remote database, which is far simpler) under Docker (on a Mac, explained below):

  1. $ docker pull jeffknupp/sandman2
  2. $ docker run -d -e DB_TYPE=postgres -e DB_DRIVER=psycopg2 -e USERNAME=jknupp -e DB_HOST=host.docker.internal -e DATABASE=jknupp -e DB_PORT=5432 -p 9000:5000 sandman2
  3. $ curl localhost:9000/meta or open a browser to http://localhost:9000/admin/

Note, DB_HOST=host.docker.internal is only necessary for databases that reside on the host system (and the value only works on macOS). To connect to a database on a remote machine, simply replace that value with the machine's IP or hostname.

Parameters

Here are the parameters available to specify your connection information and their meaning:

  • $DB_TYPE - The type of RDBMS to connect to (e.g. postgres or mysql)
  • $DB_DRIVER - The name of the Python library to use as a driver (e.g. psycopg2 or pymysql)
  • $USERNAME - Database username
  • $PASSWORD - Database password
  • $DB_HOST - Database IP or hostname
  • $DB_PORT - Database port
  • $DATABASE - Name of database to connect to

Pass each value separately to the docker run command with -e <VARIABLE>=<VALUE>. Not all are required, but which ones are required differs based on your target RDBMS.

Author: jeffknupp
Source Code: https://github.com/jeffknupp/sandman2
License: Apache-2.0 License

#python #restful #api 

Sandman2: Automatically Generate A RESTful API Service For Database

Flask RESTful: Simple Framework for Creating REST APIs

Flask-RESTful

Flask-RESTful provides the building blocks for creating a great REST API.

Flask-RESTful is an extension for Flask that adds support for quickly building REST APIs. It is a lightweight abstraction that works with your existing ORM/libraries. Flask-RESTful encourages best practices with minimal setup. If you are familiar with Flask, Flask-RESTful should be easy to pick up.

User Guide

You'll find the user guide and all documentation here

Author: flask-restful
Source Code: https://github.com/flask-restful/flask-restful
License: BSD-3-Clause License

#python #flask #restful 

Flask RESTful: Simple Framework for Creating REST APIs

Restit: A Go Library Help Testing Your RESTful API Application

RESTit

A Go micro-framework to help writing RESTful API integration test

Package RESTit provides helps to those who want to write an integration test program for their JSON-based RESTful APIs.

The aim is to make these integration readable highly re-usable, and yet easy to modify.

Versions

Different version of RESTit library will go under different directory.

This library is under major rewrite. The old version (version 1) is consider API stable and will only receive bug fixes. Development version (version 2) is under active development and will have new features (which may break API).

For more information, please read the README.md of these version:

Bug Reports

To report issue, please visit the issue tracker.

And of course, patches and pull requests are most welcome.

Author: Go-restit
Source Code: https://github.com/go-restit/restit 
License: GPL-3.0 license

#go #golang #restful 

Restit: A Go Library Help Testing Your RESTful API Application