На днях я начал работать над личным проектом, включающим 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.