Laravel中POST请求成功后如何正确发起GET请求调用API
问题概述
基于Laravel+AJAX搭建、部署于AWS的简历解析系统,核心逻辑为上传doc/docx/pdf格式简历到AWS S3后,拿到文件访问URL传递给apilayer简历解析接口,输出结构化JSON数据。
目前S3文件上传流程运行正常,但调用解析接口时返回如下错误:
Client error:
POST https://api.apilayer.com/resume_parser/url?url=https://--.s3.eu-central-1.amazonaws.com/file/--resulted in a403 Forbiddenresponse:{"message":"You cannot consume this service"}
排查发现发往apilayer的请求误用了POST方法(接口要求GET方法),同时存在前端直接调用第三方接口的逻辑漏洞、S3文件权限配置、请求缺少认证信息等问题。
修复步骤
1. 后端逻辑调整
(1)修复现有上传接口语法问题&优化返回格式
原store方法末尾多了一个多余的},会触发PHP语法错误,先删除。同时将原直接返回URL字符串的逻辑改为返回结构化JSON,方便前端取值;如果S3桶为私有权限,替换为生成5分钟有效期的临时预签名URL,既不用开放S3公共读权限,也能保证apilayer服务端可以正常拉取文件。
修正后的store方法代码:
public function store(Request $request) { $request->validate([ 'file' => 'required|mimes:doc,docx,pdf|max:2048', ]); $fileName = time() . '.' . $request->file->extension(); $path = $request->file->storeAs('file', $fileName, 's3'); CVupload::create(['name' => $fileName]); // 私有桶用临时预签名URL,公共桶可沿用原来的Storage::disk('s3')->url($path) $urlcv = Storage::disk('s3')->temporaryUrl( $path, now()->addMinutes(5) ); return response()->json(['url' => $urlcv]); }
(2)新增后端代理调用apilayer的接口
禁止从前端直接调用apilayer接口:一是会触发浏览器跨域限制,二是API Key直接暴露在前端代码中极易被盗刷。把第三方接口调用逻辑移到后端,API Key存储在.env环境变量中,不硬编码到代码里。
首先在.env文件中添加配置项:
APILAYER_KEY=你在apilayer控制台获取的简历解析服务密钥
在config/services.php中添加服务配置:
'apilayer' => [ 'key' => env('APILAYER_KEY'), 'endpoint' => 'https://api.apilayer.com/resume_parser/url' ]
在控制器中新增解析方法,明确使用GET方法请求apilayer接口,携带要求的apikey认证头:
use Illuminate\Support\Facades\Http; public function parseResume(Request $request) { $request->validate([ 'url' => 'required|url' ]); $response = Http::withHeaders([ 'apikey' => config('services.apilayer.key') ])->get(config('services.apilayer.endpoint'), [ 'url' => $request->input('url') ]); if ($response->failed()) { return response()->json([ 'message' => '解析接口请求失败', 'detail' => $response->json() ], $response->status()); } return response()->json($response->json()); }
最后添加路由,和原有上传路由放在同一组即可,自动继承CSRF认证:
Route::get('resume/parse', [YourResumeController::class, 'parseResume'])->name('resume.api');
2. 前端AJAX逻辑调整
原AJAX代码存在两个问题:一是上传成功后没有正确读取后端返回的结构化URL字段,二是调用解析接口的请求没有配置成功/失败回调,无法处理返回结果或捕获异常。
修正后的前端脚本代码:
<script type="text/javascript"> $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); $('#file-upload').submit(function(e) { e.preventDefault(); let formData = new FormData(this); $('#file-input-error').text(''); $('#parse-result').text('文件上传中,请稍候...'); $.ajax({ type: 'POST', url: "{{ route('resume.store') }}", data: formData, contentType: false, processData: false, success: (response) => { if (response.url) { this.reset(); $("#showResponseArea span").html(response.url); $('#parse-result').text('文件上传成功,正在解析简历...'); // 调用本地后端解析接口,不直接请求第三方 $.ajax({ type: 'GET', url: "{{ route('resume.api') }}", dataType: 'json', data: { 'url': response.url }, success: (parseResult) => { // 此处按业务需求渲染解析后的结构化JSON数据即可 console.log('简历解析结果', parseResult); $('#parse-result').text(JSON.stringify(parseResult, null, 2)); }, error: (xhr) => { $('#file-input-error').text('简历解析失败:' + (xhr.responseJSON?.message || '未知错误')); $('#parse-result').text(''); } }) } }, error: function(response) { $('#file-input-error').text(response.responseJSON?.message || '文件上传失败'); $('#parse-result').text(''); } }); }); </script>
额外排查项
- 确认apilayer账号已经激活简历解析服务,账户有剩余调用额度,复制的API Key没有多余空格或错漏字符
- 如果使用S3公共读权限,确认桶策略没有拦截apilayer服务端的请求;生产环境强烈建议使用临时预签名URL,不要开放S3公共读写权限
- 确认Laravel的
verify_csrf_token中间件没有把上传、解析接口排除在外,避免CSRF认证失败
内容的提问来源于stack exchange,提问作者user15226613

