[Grails]namespaceを使って同名コントローラを使い分ける

#概要
Grailsでは、namespaceという機能を使って、同名のコントローラを別パッケージに分けて、使い分けることができます。
使いどころとして、例えば`User`というドメインが有る場合にscaffold用Controllerを生成すると`UserController.groovy`となります。
でも普通のユーザ用の個人ページ用にも`UserControler.groovy`という名前って使いそうですよね？
そこで、今回のnamespaceを利用することでパッケージを分けて、同名のControllerが混在する環境を実現することができます。

#コントローラの設置
前提条件：
アプリケーション名（デフォルトパッケージ）：ntest

テスト用に3つのコントローラを作成します。すべて同名のコントロラーですが、パッケージが異なります。
最初のコントロラー(ntest/TestController.groovy)は通常のコントローラです。

```groovy:ntest/grails-app/controllers/ntest/TestController.groovy
package ntest

class TestController {

    def index() {
        render "Test Controller in ntest Package!"
    }
}
```

```groovy:ntest/grails-app/controllers/ntest/hoge/TestController.groovy
package ntest.hoge
class TestController {

    static namespace = 'hoge'
    
    def index() {
        render "Test Controller in ntest.hoge Package!"
    }
}


```

```groovy:ntest/grails-app/controllers/ntest/piyo/TestController.groovy
package ntest.piyo

class TestController {

    static namespace = 'piyo'

    def index() {
        render "Test Controller in ntest.piyo Package!"
    }
}
```

ntest.hogeパッケージとntest.piyoパッケージのコントローラで少し見慣れない`static namespace = 'hoge（piyo）'`という記述があります。
これがnamespaceの正体で、この値を`UrlMappings.groovy`で利用します。

#URLの設定

```groovy:grails-app/conf/UrlMappings.groovy
class UrlMappings {

	static mappings = {
        "/$controller/$action?/$id?(.$format)?"{
            constraints {
                // apply constraints here
            }
        }

        // この2行を追加した
        "/namespace/a/$controller/$action?/$id?(.$format)?" (controller: "test", namespace: 'hoge')
        "/namespace/b/$controller/$action?/$id?(.$format)?" (controller: "test", namespace: 'piyo')
        "/"(view:"/index")
        "500"(view:'/error')
	}
}
```
コードを見れば一目瞭然ですが、各URLに紐づくコントローラの指定と同時にnamespaceも指定しています。
ここで利用する値が、先ほどコントローラ内で記述した`static namespace = 'hoge（piyo）'`の値になります。
なお、`"/$namespace/$controller/$action?"()`としておけば、ネームスペース名/コントローラ名/アクション名/で統一的にアクセスできるようになります。

# 確認
実際にGrailsを起動して、それぞれのコントローラにアクセスしてみると確認できます。
[http://localhost:8080/ntest/test/index](http://localhost:8080/ntest/test/index)　（通常のコントローラ）
[http://localhost:8080/ntest/namespace/a/test/index](http://localhost:8080/ntest/namespace/a/test/index)　（ntest.hogeパッケージ）
[http://localhost:8080/ntest/namespace/b/test/index](http://localhost:8080/ntest/namespace/b/test/index)　（ntest.piyoパッケージ）

それぞれ出力結果が異なることが解ると思います。
namespaceを利用することで、明らかに最適なコントローラ名なのにすでに同じものがあって泣く泣くプレフィックスなどを付けて回避するということがなくなります。
**ただし！** うっかりnamespaceをつけ忘れてしまうと、Grailsが同名のコントローラの内どちらを使うかは決まっていません。（今回のサンプルははnamespaceが付いていない同名コントローラが存在しないので問題ない。）
コンパイルエラー等で事前に察知することができないので利用する際には注意しましょう。

# リンクを生成する際の注意
公式ドキュメントなどでは、`g:link`タグに`namespace`属性をしているとありますが、少なくとも **Grails2.3.7では動作しません**
そのため、回避策としてUrlMappingsで指定した今回のURLに名前を付けてあげる必要があります。


```groovy:grails-app/confUrlMappings.groovy
class UrlMappings {

	static mappings = {
        "/$controller/$action?/$id?(.$format)?"{
            constraints {
                // apply constraints here
            }
        }

        // name 任意の名前 : 任意のURL {コントローラとかネームスペースの指定}とうフォーマット
        name hogeTest : "/namespace/a/$controller/$action?/$id?(.$format)?"{
            controller = "test"
            namespace = 'hoge'
        }
        name piyoTest: "/namespace/b/$controller/$action?/$id?(.$format)?" {
            controller = "test"
            namespace = 'piyo'
        }


        "/"(view:"/index")
        "500"(view:'/error')
	}
}

```

で、実際にgspなどでリンクを生成する際には、 **mapping** という属性に今回新たに追加したnameを指定してあげます。

```html
<g:link mapping="hogeTest" action="index">TEST</g:link><br />
<g:link mapping="piyoTest" action="index">TEST</g:link>
<g:form mapping="hogeTest">
    <g:submitButton name="click!" />
</g:form>
```

これでちゃんとnamespaceの反映されたリンクが生成されます。


# SpringSecurityCore2.0を導入している際の問題と回避方法
SpringSecurityCore2.0RCを使っていると、namespaceを指定しているコントローラに **@Secured** アノテーションを記述しても **有効になりません。**

@Securedアノテーションの指定してあるnamespaceが指定されているページにアクセスすると以下のようなメッセージが表示されます。
**Sorry, you're not authorized to view this page.**

おそらく、SpringSecurityCore2.0RC自体がnamespaceを想定していないのではないか、と思われます。（ソースを読んでいないのでただの予想）

回避方法として、`Config.groovy`に、UrlMappings.groovyで指定したnamespace用のURLにアクセスした際の挙動を`grails.plugin.springsecurity.controllerAnnotations.staticRules`に追加してあげます。

```groovy:grails-app/conf/Config.groovy
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
	'/':                              ['permitAll'],
	'/index':                         ['permitAll'],
	'/index.gsp':                     ['permitAll'],
	'/**/js/**':                      ['permitAll'],
	'/**/css/**':                     ['permitAll'],
	'/**/images/**':                  ['permitAll'],
	'/**/favicon.ico':                ['permitAll'],
    '/**/namespace/a/**':             ['ROLE_ADMIN']  // これを追加した
    '/**/namespace/b/**':             ['ROLE_ADMIN']  // これを追加した
]
```
最後の2行のURLを追加しました。
これで無事SpringSecurityCoreを導入していて、namespaceを利用することができるようになります。
このようにConfig.groovyで、指定したURLにアクセスした際の動作をControllerの@Secureアノテーション同様に制御できます。

#参考
[http://grails.jp/doc/latest/guide/single.html#namespacedControllers](http://grails.jp/doc/latest/guide/single.html#namespacedControllers)
[https://jira.grails.org/browse/GPSPRINGSECURITYCORE-246](https://jira.grails.org/browse/GPSPRINGSECURITYCORE-246)