从 PHP Laravel 到 Go Iris--路由篇

Laravel是我最喜欢的PHP Web开发框架,所以也希望可以在Go的Web框架中选择一个类似Laravel这样的好用又全栈的框架,刷了一下Beego, Echo , Gin, 以及Iris的文档,最终还是选择Iris,当然我是没有从性能角度考虑,只是从可以快速开发,且支持的特性全还有就是看着顺眼的心理选择了Iris,应该有不少PHPer像我一样使用Laravel同时在学习Go,所以为了便于Laravel开发者可以快速的转到Iris开发,我准备写一系列这两者框架的比较文章。

基本路由

Iris构建基本路由和Laravel的基本路由很像,都只需要一个URI与一个闭包:

Laravel

Route::get('foo', function () {
    return 'Hello World';
});

Iris

app.Get("/foo", func(ctx iris.Context) {
    ctx.WriteString("Hello World")
})

可用的路由方法

Iris和Laravel一样,能够响应任何的HTTP请求路由:

Laravel

Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

Iris

app.Post("/", func(ctx iris.Context){})
app.Put("/", func(ctx iris.Context){})
app.Delete("/", func(ctx iris.Context){})
app.Options("/", func(ctx iris.Context){})
app.Trace("/", func(ctx iris.Context){})
app.Head("/", func(ctx iris.Context){})
app.Connect("/", func(ctx iris.Context){})
app.Patch("/", func(ctx iris.Context){})

对于注册一个可以响应多个HTTP请求的路由两者同样都支持

Laravel

Route::match(['get', 'post'], '/', function () {
});

Route::any('foo', function () {
});

Iris

app.Any("/", func(ctx iris.Context){})

路由参数

必填参数

Iris的定义路由必填参数和Laravel差不多,这里需要注意下Laravel的路由参数不能包含 - 字符,可以用用下划线 _ 替换

Laravel

Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

Iris

app.Get("/user/{id}", func(ctx iris.Context) {
    userID, err := ctx.Params().GetInt("userid")
    if err != nil {
        //
    }
    ctx.Writef("User %d", userID)
})

正则约束

Iris同样支持正则约束,直接在路由参数中设置很方便,Laravel的路由设置可以通过where链式方法,也很直观:

Laravel

Route::get('user/{name}', function ($name) {
})->where('name', '[A-Za-z]+');

Iris

app.Get("/user/{name:string regexp(^[A-Za-z]+)}", func(ctx iris.Context) {})

全局约束

Iris没有对路由参数全局约束,Laravel可以通过RouteServiceProviderboot中定义pattern来定义,但是Iris可以通过标准的macro或者自定义macro来约束参数:

Laravel

public function boot()
{
    Route::pattern('id', '[0-9]+');
    parent::boot();
}

Iris

app.Get("/profile/{id:int min(3)}", func(ctx iris.Context) {
})
//当然标准的 macro 除了int 还有string,alphabetical,file,path当然你也可以自己注册一个macro来改变约束规则
app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool {
    return func(paramValue string){ return argument == paramValue }
})
//实现app.Macros().类型.RegisterFunc()方法即可

命名路由

Iris和Laravel都支持给指定的路由命名,而且方式很像:

Laravel

Route::get('user/profile', function () {
})->name('profile');

Iris

app.Get("/user/profile", func(ctx iris.Context) {
}).Name = "profile"

为命名生成链接

Laravel通过route()方法来根据路由名和参数生成URL链接,Iris也提供了对应的方法来生成链接:

Laravel

$url = route('profile', ['id' => 1]);

Iris

rv := router.NewRoutePathReverser(app)
url := rv.URL("profile",1)
//URL(routeName string, paramValues ...interface{})
//Path(routeName string, paramValues ...interface{} 不包含host 和 protocol

检查当前路由

检查当前请求是否指向了某理由:

Laravel

if ($request->route()->named('profile')) {}

Iris

if ctx.GetCurrentRoute().Name() == "profile" {}

路由组

路由组可以共享路由属性,Laravel所支持的路由组属性Iris也基本都支持,而且很像,都很优雅.

中间件

通过中间件可以对路由请求约束,对于日常开发非常有用,比如做Auth验证,可以直接通过中间件来做隔离:

Laravel

Route::middleware(['auth'])->group(function () {
    Route::get('user/profile', function () {
        // 使用 auth 中间件
    });
});

Iris

authentication := basicauth.New(authConfig)
needAuth := app.Party("/user", authentication)
{
    needAuth.Get("/profile", h)
}

子域名路由

在Laravel中,路由组可以用作子域名的通配符,使用路由组属性的 domain 键声明子域名。Iiris可以通过Party来直接设置:

Laravel

Route::group(['domain' => '{subdomain}.myapp.com'], function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});

Iris

subdomain := app.Party("subdomain.")
{
    subdomain.Get("/user/{id}", func(ctx iris.Context) {
        //
    })
}

dynamicSubdomains := app.Party("*.")
{
    dynamicSubdomains.Get("/user/{id}", func(ctx iris.Context) {
        //
    })
}

路由前缀

给URL添加前缀,Laravel 通过prefix,Iris还是通过Party就可以了

Laravel

Route::prefix('admin')->group(function () {
    Route::get('users', function () {
        // 匹配包含 "/admin/users" 的 URL
    });
});

Iris

adminUsers := app.Party("/admin")
{
    adminUsers.Get("/users", , func(ctx iris.Context) {
        // 匹配包含 "/admin/users" 的 URL
    })
}

好了,这两个web框架的路由基本比较和应用就这些了,还有一些比如控制器路由和如何自定义中间件等在后续再写吧,或者请自行查阅文档,以上内容如有错误请指出。

转载请注明: 转载自Ryan是菜鸟 | LNMP技术栈笔记

如果觉得本篇文章对您十分有益,何不 打赏一下

谢谢打赏

本文链接地址: 从PHP Laravel 到 Go Iris--路由篇

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 14

现在我司大部分 都还没有用框架,他们感觉 不需要,喜欢做重复的工作

6年前 评论
Destiny

看着很不错哟

6年前 评论
Ryan

@Destiny 有没有想写go的冲动,哈哈

6年前 评论
Summer

Ryan 明明是高手

6年前 评论
Ryan

明明是菜鸟 :smile:

6年前 评论
Ryan

@motecshine 不用框架也无可厚非,就是如果对于企业级应用来说后期维护和解耦是个头疼的问题吧

6年前 评论

@Ryan 很头疼,新的项目在用beego。老的项目 代码到处是 c-c c-v ,维护成本很大,很难受

6年前 评论

看了这篇文章,决定先尝试下Iris:smile:

6年前 评论

先star,回来看看

6年前 评论

irisjwt方案和php在签名(signature)的生成上似乎很难相通
比如下例

$header = [
  'typ'=> 'JWT',
  'alg'=> 'HS256'
]

$payload =[
  "sub"=>"1234567890",
  "name"=> "John Doe",
  "admin"=> true
]

假设密钥是secret
为方便直接给出base64后的$header . "." . $payload

$left=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

laraveljwt-auth包或原生php实现签名
$signature = hash_hmac("sha256", $left, $secret)

得到4c9540f793ab33b13670169bdf444c1eb1c37047f18e861981e14e34587b1e04

然后使用irisjwt方案, 为缩短篇幅我直接将他的实现思路写在下面

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "fmt"
    "strings"
    "encoding/base64"
)

func main() {
    //hmac, use sha256
    key := []byte("secret")
    mac := hmac.New(sha256.New, key)
    mac.Write([]byte("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9"))
    raw := mac.Sum(nil)
    fmt.Println(raw)
    fmt.Printf("%x\n",raw) 
    fmt.Printf("%s\n", base64.URLEncoding.EncodeToString(raw))  //其方案最终输出
    //todo 然后去掉=符号就完工了
}

他的结果是TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ=, 当然这个等于号=是我懒, 没有去掉, 实际在他的方案里要去掉的
其中fmt.Printf("%x\n", raw)输出的是4c9540f793ab33b13670169bdf444c1eb1c37047f18e861981e14e34587b1e04, 和PHP相同

所以想请问: 在如上方案, 如何做到使PHP最终与iris的输出相同呢(当然不是iris改用fmt.Print("%x", raw)啦- -)

6年前 评论
Ryan

@泽 这个我倒是一直没有在意,谢谢指出,不过实际状况中不会有这种php和iris混用jwt的应用场景吧

6年前 评论

@Ryan 目前却是没有碰到哦, 也是偶然发现的。
但是确实感到一阵混乱~

6年前 评论

@Ryan 写的很好, 不知道有没有了解过Revel, 好像和Laravel更接近, 也是类RoR的

5年前 评论

@ken_lin 看了下Revel,貌似好久没更新了,是不是凉了?

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!