如何在Grails中的控制器中调用服务?

时间:2014-05-12 16:29:34

标签: grails oauth

我在Grails中编写REST客户端,我需要使用OAUTH与第三方CRM集成,因此我使用HTTP-Builder。我想将OAUTH内容抽象为服务,并使令牌和instance_url可用于控制器和视图。

这是我的服务:

import org.springframework.transaction.annotation.Transactional;

import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseException
import static groovyx.net.http.ContentType.URLENC
import static groovyx.net.http.Method.POST
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON

class APIConnectorService {

    def sf_login_domain = 'STUFFTHATWORKS'
    def instance_domain
    def access_token = ''
    def consumer_key =    'STUFFTHATWORKS'
    def consumer_secret = 'STUFFTHATWORKS'
    def auth_username = 'STUFFTHATWORKS'
    def auth_password = 'STUFFTHATWORKS'

    def serviceMethod() {

        //Request Access_token and instance domain for work
            def http = new HTTPBuilder(sf_login_domain)
            def postBody = [
                grant_type: 'password',
                client_id: consumer_key,
                client_secret: consumer_secret,
                username: auth_username,
                password: auth_password+auth_security_token
                ]
        try{

            http.post( path : 'services/oauth2/token',
                      body : postBody,
                      requestContentType: URLENC) { resp, json ->
                            access_token = json.access_token
                            instance_domain = json.instance_url +"/"
                        }

        }catch(HttpResponseException e){
            println "Error code: ${e.statusCode}"
            println "Post form: $postBody"
        }

        println "Access Token $access_token"
        println "Instance domain $instance_domain \n"

  }
  }

这是我的控制器

import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseException
import static groovyx.net.http.ContentType.URLENC
import static groovyx.net.http.Method.POST
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON



class APIConnectorController {

def APIConnectorService

def index(){


    http = new HTTPBuilder(APIConnectorService.serviceMethod())
    http.request(GET,JSON) { req ->
      uri.path = 'services/data/v28.0/'
      headers['Authorization'] = "Bearer $access_token"


    response.success = { resp, json  ->
        assert resp.status == 200
        json.each{ key,value ->
        render(view:"index", model: [json: json])
            }
        }

    response.failure = { resp, json ->
        println resp.status
        println json.errorCode
        println json.message
      } 
    }
    }
 }

因此,当我将所有服务代码直接包含在控制器中时,它的行为方式与我一样,但正如我所说,我想将OAUTH令牌生成抽象为服务(至少,这个对我来说最有意义。)

尝试上述配置时出现此错误

ERROR errors.GrailsExceptionResolver  - MissingMethodException occurred when processing request: [GET] /SFAPITest/APIConnector
No signature of method: sfapitest.APIConnectorController.HTTPBuilder() is applicable for argument types: (null) values: [null]. Stacktrace follows:
Message: No signature of method: sfapitest.APIConnectorController.HTTPBuilder() is applicable for argument types: (null) values: [null]

UPDATE !!

我将访问令牌和实例域从服务传递到控制器但现在我已经#得到错误:

ERROR errors.GrailsExceptionResolver  - MissingMethodException occurred when processing request: [GET] /SFAPITest/ApiConnector
No signature of method: sfapitest.ApiConnectorController.HTTPBuilder() is applicable for argument types: (java.lang.String) values: [https://scholastica--FullBox.cs16.my.salesforce.com/]. Stacktrace follows:
Message: No signature of method: sfapitest.ApiConnectorController.HTTPBuilder() is applicable for argument types: (java.lang.String) values: [https://ourschool--FullBox.cs16.my.forcefulCRM.com/]

这是我更新的服务

package sfapitest

import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseException
import static groovyx.net.http.ContentType.URLENC
import static groovyx.net.http.Method.POST
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON

class ApiConnectorService {

def transactional = false

def sf_login_domain = 'STUFFTHATWORKS'
def instance_domain
def access_token = ''
def consumer_key = 'STUFFTHATWORKS'
def consumer_secret = 'STUFFTHATWORKS'
def auth_username = 'STUFFTHATWORKS'
def auth_password = 'STUFFTHATWORKS'
def auth_security_token = 'STUFFTHATWORKS'
def api_token_path = 'services/oauth2/token'
def oauthObject

 def serviceMethod() {

    //Request Access_token and instance domain for work
        def http = new HTTPBuilder(sf_login_domain)
        def postBody = [
                grant_type: 'password',
                client_id: consumer_key,
                client_secret: consumer_secret,
                username: auth_username,
                password: auth_password+auth_security_token
                ]
        try{

            http.post( path : api_token_path,
                       body : postBody,
                       requestContentType: URLENC) { resp, json ->
                             access_token = json.access_token
                             instance_domain = json.instance_url +"/"
                        }

                    oauthObject = [access_token, instance_domain]

        }catch(HttpResponseException e){
            println "Error code: ${e.statusCode}"
            println "Post form: $postBody"
        }

    return oauthObject
}
}

因此,感谢下面的建议,我将access_token和instance _domain的数组返回给serviceMethod()。

这是我的控制人员:

package sfapitest


import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseException
import static groovyx.net.http.ContentType.URLENC
import static groovyx.net.http.Method.POST
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON



class ApiConnectorController {

def apiConnectorService

def index(){

    def access_token = apiConnectorService.serviceMethod()[0]
    def instance_domain = apiConnectorService.serviceMethod()[1]


    http = HTTPBuilder(instance_domain)
    http.request(GET,JSON) { req ->
      uri.path = 'services/data/v28.0/'
      headers['Authorization'] = "Bearer $access_token"


    response.success = { resp, json  ->
        assert resp.status == 200
        json.each{ key,value ->
        render(view:"index", model: [json: json])
            }
        }

    response.failure = { resp, json ->
        println resp.status
        println json.errorCode
        println json.message
      } 
    }
}
}

MissingMethodException可能意味着我没有正确数量的参数。我可以从cotroller渲染access_token和instance_domain,它们显示正确的值。关于如何使这项工作更优雅的任何提示和指示将不胜感激。我的目标是创建一个RESTful / OAUTH应用程序,可用于各种第三方Web服务集成。

5 个答案:

答案 0 :(得分:1)

您将APIConnectorService.serviceMethod()作为

中的参数传递
http = new HTTPBuilder(APIConnectorService.serviceMethod())

您的方法serviceMethod() returns没有任何内容,以至于我认为您收到此错误的原因。

serviceMethod()应该返回Object defaultURI

HttpBuilder API

HTTPBuilder()

使用null默认URI创建新实例。

HTTPBuilder(Object defaultURI)

提供一个默认URI,用于所有不明确接受URI参数的请求方法。

HTTPBuilder(Object defaultURI, Object defaultContentType)

提供一个默认URI,用于所有不明确接受URI参数的请求方法,以及用于请求编码和响应解析的默认内容类型。

答案 1 :(得分:1)

好的,经过大量试验,错误和解析HTTPBuilder文档后,最后一部分是在我的控制器中正确定义新的HTTPBuilder()实例。

    def http = new HTTPBuilder(instance_domain)
    http.request(GET,JSON) { req ->
    uri.path = 'services/data/v28.0/'
    headers['Authorization'] = "Bearer $access_token"

现在允许我从控制器中抽象出OAUTH组件。我绝对肯定有一个更优雅的解决方案,我正在努力实现。我很想听听有关如何清理它的一些建议。

  • 我是否需要服务和控制器中的导入?
  • 可以将OAUTH内容放入config.groovy吗?

答案 2 :(得分:0)

我认为问题在于您宣布服务尝试使用

的方式

def aPIConnectorService

而不是

def APIConnectorService

最好将服务名称更改为 ApiConnectorService ,并在控制器中声明如下

def apiConnectorService

答案 3 :(得分:0)

您的serviceMethod的最后一个操作是println,它导致返回null。 要访问http-builder对象,您必须在成功发布请求后返回它。

答案 4 :(得分:0)

可能是因为没有遵循正确的命名约定。

第一个建议是通过遵循类命名约定来更改服务名称。

ApiConnectorService

然后在控制器中为您的服务使用驼峰案例声明。

def apiConnectorService

Grails使用其约定来查找名称及其类。

也可以尝试使用

APIConnectorService apiConnectorService