Статьи

CORS в Grails 3 с использованием перехватчика

На днях я начал работать над личным проектом, включающим Angular 1.x и последнюю версию Grails (3.0.1). Я создал контроллер Grails с методом, который возвращал список объектов класса домена в виде JSON, в то время как на стороне Angular я написал служебный метод для выполнения HTTP-запроса на получение этого JSON. Но поскольку HTTP-запрос пришел из другого домена, он был запрещен: мне нужно было поручить Grails принять запрос через CORS (совместное использование ресурсов из разных источников).

После некоторых проб и ошибок я предложил решение с использованием артефакта- перехватчика, представленного в Grails 3 (примечание для пользователей, не относящихся к Grails: «артефакт» — это правильное написание термина Grails для определенных объектов). Я создал RestInterceptor, который добавит необходимые заголовки CORS к ответу:

package demo

class RestInterceptor {

    RestInterceptor() {
        //The widgets controller contains the method that returns the needed JSON
        match( controller: "widgets" )
    }

    boolean before() {
        //My Angular app's domain is local.test, so it needs to be the allowed origin
        header( "Access-Control-Allow-Origin", "http://local.test" )
        header( "Access-Control-Allow-Credentials", "true" )
        header( "Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE" )
        header( "Access-Control-Max-Age", "3600" )
        true
    }

    boolean after() { true }

}

Несколько заметок из экспериментов, приведших к приведенному выше примеру:

  • Как отмечено в документации по перехватчикам, перехватчики сопоставляются с одноименными контроллерами. Что не указано явно, если вы создаете Interceptor, который НЕ соответствует контроллеру (в моем приложении Grails нет «RestController»), вам нужно определить сопоставление, как в примере выше, или Grails выдаст ошибку ,

  • Итак, что касается предыдущего пункта, если бы мне нужно было только разрешить CORS для методов в моем WidgetsController, я мог бы просто создать перехватчик с именем WidgetsInterceptor и оставить его без соответствия. Но гораздо более вероятно, что у меня будет несколько контроллеров, отвечающих на запросы REST, поэтому имеет смысл иметь один перехватчик, который может включать CORS для всех соответствующих запросов на основе логики сопоставления.

  • Я столкнулся с неожиданным поведением при попытке определить разумное сопоставление (согласно поддерживаемым аргументам сопоставления ), которое будет работать с несколькими контроллерами без необходимости перечислять каждый контроллер:

    • uri:   Я подумал, что, возможно, мне удастся создать префикс URI типа «/ rest» или «/ api», который я мог бы использовать в операторах UrlMapping, чтобы объявить, какие сопоставления позволят CORS. Но я обнаружил, что использование аргумента «uri» по существу соответствует каждому запросу, даже если рассматриваемый URI не существует. Это было похоже на использование matchAll (). Либо я что-то упускаю (в этом случае документация должна дать больше рекомендаций), либо есть ошибка в этом поведении.

    • Метод:   Попытка перехватчика добавить заголовки CORS к каждому входящему запросу «GET». Не сработало, но, по крайней мере, не получилось, в отличие от сопоставления всех входящих запросов, таких как вышеупомянутый аргумент «uri».

    • namespace:   Аргумент namespace работает, но только если у вас есть пространство имен, указанное в соответствующем операторе UrlMapping, а также в контроллере . Пример сопоставления URL:

//The Angular request is made to "http://localhost:8080/rest/listWidgets"
'/rest/listWidgets' {
    controller = 'widgets'
    //Same namespace set as a static property in the WidgetsController
    namespace = 'restSpace'
    //The controller method that returns the JSON
    action = 'listWidgets'
 }
  • Grails 3 по-прежнему поддерживает более старые методы контроллера beforeInterceptor () и afterInterceptor () . Я попытался добавить заголовки CORS к ответу, используя метод beforeInterceptor () в моем WidgetsController, но безуспешно. Вполне возможно, что я просто не все сделал правильно, но я нашел проблему Grails GitHub, которая утверждала, что beforeInterceptor () не работал должным образом. В любом случае, автономный перехватчик кажется лучшим вариантом.

  • Для людей, все еще использующих Grails 2.x, существует плагин CORS, который позволяет вам управлять поведением CORS через настройки в Config.groovy. Не удивлюсь, если кто-нибудь в конце концов разработает подобный плагин для Grails 3.