使用 RESTClient 请求 PaymentSpring 时返回 Not Found
支付阿杰
结论
Not Found 表示服务器返回了 HTTP 404。最可能的原因是 path: '/charge' 以 / 开头,RESTClient 因而覆盖了基础地址中的 /api/v1,实际请求的是:
https://api.paymentspring.com/charge
预期地址则是:
https://api.paymentspring.com/api/v1/charge
解决方法有两种:在请求中使用完整的 API 路径,或者让基础地址以 / 结尾,同时使用不以 / 开头的相对路径。
推荐修改
可以只将域名设为基础地址,并在请求中写明完整路径:
@Grab(
group = 'org.codehaus.groovy.modules.httpbuilder',
module = 'http-builder',
version = '0.7'
)
import groovyx.net.http.HttpResponseException
import groovyx.net.http.RESTClient
import static groovyx.net.http.ContentType.URLENC
def paymentSpring() {
def username = 'test_XXXXXXXXXXXXXXXXXXXX'
def https = new RESTClient('https://api.paymentspring.com')
https.auth.basic(username, '')
def charge = [
card_owner_name: 'test test',
card_number : '345829002709133',
card_exp_month : '12',
card_exp_year : '2015',
csc : '999',
amount : '100.00',
email_address : 'ujdieu@yahoo.com',
send_receipt : true
]
try {
def resp = https.post(
path: '/api/v1/charge',
requestContentType: URLENC,
body: charge
)
println "HTTP ${resp.status}"
println resp.data
} catch (HttpResponseException ex) {
println "HTTP ${ex.statusCode}: ${ex.message}"
println ex.response?.data
}
}
paymentSpring()
也可以保留原来的基础地址,但需要将它写成目录形式,并传入相对路径:
def https = new RESTClient('https://api.paymentspring.com/api/v1/')
def resp = https.post(
path: 'charge',
requestContentType: URLENC,
body: charge
)
这里应当写:
path: 'charge'
不要写成:
path: '/charge'
为什么会出现 404
按照 URL 解析规则,以 / 开头的路径通常会被视为从域名根目录开始的绝对路径。例如:
基础地址:https://api.paymentspring.com/api/v1
请求路径:/charge
两者组合后可能得到的不是 /api/v1/charge,而是 /charge。服务器虽然收到了请求,但这个地址没有对应的 API 资源,因此返回 Not Found。
这通常不是身份认证或表单参数造成的。认证失败更常返回 401 Unauthorized 或 403 Forbidden,字段错误一般会返回 400、422 等验证错误,而路径不存在才会返回 404。
改进异常输出
原代码只打印了异常对象,之后又打印了尚未赋值的 resp,所以输出会是:
groovyx.net.http.HttpResponseException: Not Found
null
调试时可以改为输出状态码、最终请求地址和响应正文:
try {
def resp = https.post(
path: '/api/v1/charge',
requestContentType: URLENC,
body: charge
)
println resp.status
println resp.data
} catch (HttpResponseException ex) {
println "Status: ${ex.statusCode}"
println "URI: ${ex.response?.context?.getAttribute('http.target_host')}"
println "Response: ${ex.response?.data}"
}
不同版本的 HTTPBuilder 暴露的上下文字段可能不同,因此不一定能取得 URI 调试信息。必要时,可以启用 HTTPBuilder 或 Apache HttpClient 的请求日志,确认最终发送的 URL。
还需要检查的事项
修正路径后,如果接口返回参数验证错误,还需要检查:
card_exp_year: '2015'已经过期,应替换为接口测试环境允许的有效测试数据。- 测试环境必须使用 PaymentSpring 提供的测试 API Key 和测试卡号,不要提交真实银行卡信息。
amount的格式和单位应以当前 PaymentSpring API 文档为准。有些支付接口使用货币小单位,有些则接受带小数的金额字符串。Content-Type已通过requestContentType: URLENC指定,通常不用再手动添加同名请求头。- 如果当前 PaymentSpring 文档中的 endpoint 已不再是
/api/v1/charge,应以账号对应的环境和当前官方文档列出的路径为准。
先修复 URL 拼接问题。确认请求确实发送到了 /api/v1/charge 后,再检查认证信息和业务参数。
备注:内容仅供参考。
