Angular 4 App调用Play Framework Web App的CORS问题

Posted

技术标签:

【中文标题】Angular 4 App调用Play Framework Web App的CORS问题【英文标题】:CORS Issue with Angular 4 App calling Play Framework Web App 【发布时间】:2018-02-07 08:18:01 【问题描述】:

我有一个基于 Angular 4 和 Node.JS 的前端应用程序堆栈,然后调用使用 Play Framework 构建的后端 API。我现在遇到了 CORS 过滤器的问题。我在 Play 框架上启用了 CORS 过滤器,如下所示:

object MyController extends Controller 

    implicit class RichResult (result: Result) 
    def enableCors =  result.withHeaders(
      "Access-Control-Allow-Origin" -> "*"
      , "Access-Control-Allow-Methods" -> "OPTIONS, GET, POST, PUT, DELETE, HEAD"   // OPTIONS for pre-flight
      , "Access-Control-Allow-Headers" -> "Accept, Content-Type, Origin, X-Json, X-Prototype-Version, X-Requested-With" //, "X-My-NonStd-Option"
      , "Access-Control-Allow-Credentials" -> "true"
    )
  

  def powerPlantDetails(id: Int) = Action.async 
    dbService.powerPlantById(id).flatMap 
      case None =>
        Future.successful(
          NotFound(s"HTTP 404 :: PowerPlant with ID $id not found").enableCors
        )
      case Some(powerPlantRow) =>
        Future.successful(
          Ok(Json.prettyPrint(
            Json.toJson(toPowerPlantConfig(powerPlantRow)))
          ).enableCors
        )
    
  

但是当我从 Angular 4 应用程序调用此端点时,我可以从 Play Framework 的服务器日志中看到它确实是作为 OPTIONS 请求进入的:

[info] application - OPTIONS /powerPlants?onlyActive=false&page=1 took 0ms HTTP status >> 404
[info] application - OPTIONS /powerPlants?onlyActive=false&page=1 took 0ms HTTP status >> 404

这是为什么?我如何确保我的 Angular 4 应用正在执行 GET 而不是 OPTIONS?

编辑:如果我从 Chrome 中的高级 REST 客户端调用我的 Play Framework 应用程序,我会看到以下内容:

 application - GET /powerPlants?onlyActive=false&page=1 took 25694ms HTTP status >> 200
(Access-Control-Allow-Origin,*)
(Access-Control-Allow-Headers,Accept, Content-Type, Origin, X-Json, X-Prototype-Version, X-Requested-With)
(Access-Control-Allow-Methods,OPTIONS, GET, POST, PUT, DELETE, HEAD)
(Access-Control-Allow-Credentials,true)

可以看出它实际上是在发出 GET 请求,但如果我从我的 Angular 应用程序调用相同的端点,它就无法做到这一点,这对我来说很奇怪!

【问题讨论】:

我认为 CORS 必须先执行一个 OPTIONS 才能查看服务器的功能以及返回的标头。由于它返回 404,我猜想在执行 GET 之前停止请求。也许您有一些标题或内容类型使其不是“简单请求”? CORS 我没有任何特殊的标题!我将使用我在 Play Framework 中设置的所有标题更新我的帖子! 没用过play框架,看看this吧?您的 Angular 应用程序中的某些内容导致它不是一个简单的请求,因此它正在发送 OPTIONS 预检以检查服务器功能,并且您的服务器正在为 OPTIONS 请求返回 404。我认为您要么必须弄清楚如何使其成为简单的请求(小心地通过该 CORS 链接),要么让您的服务器处理 OPTIONS... 让我的 Play 服务器处理 GET 请求的 OPTIONS 是我将避免的事情。我还有另一个 Angular 应用程序正在向我的 Play 服务器中的同一端点发出请求,并且正在通过,但是这个新的 Angular 应用程序似乎有问题! 然而,两者之间的唯一区别是,在新的 Angular 应用程序中,当我执行 http.get(...) 时,我设置了一些标题。也许我会尝试删除它们并试一试! 【参考方案1】:

您知道 Play 以过滤器的形式提供内置的 CORS 处理吗?详情请见https://www.playframework.com/documentation/2.6.x/CorsFilter。

这消除了手动处理 CORS 的负担。

【讨论】:

【参考方案2】:

看来我已经找到了问题所在。这是我在 Angular 应用程序中的 get 函数:

get(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> 
    return this.http.get(`$environment.api_url$path`,  headers: this.setHeaders(), search: params )
      .catch(this.formatErrors)
      .map((res: Response) => res.json());
  

这就是 setHeaders 函数:

private setHeaders(): Headers 
    const headersConfig = 
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Request-Headers': 'content-type'
  ;

我必须完全删除它!所以我的 get 现在看起来像:

get(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> 
    return this.http.get(`$environment.api_url$path`,  search: params )
      .catch(this.formatErrors)
      .map((res: Response) => res.json());
  

我不确定这是否正确,但这确实可以解决问题!我可以从我的 Play 服务器日志中看到以下内容:

[info] application - GET /powerPlants?onlyActive=false&page=1 took 1539ms HTTP status >> 200
[info] application - GET /powerPlants?onlyActive=false&page=1 took 1549ms HTTP status >> 200
[info] application - GET /powerPlants?onlyActive=false&page=1 took 1607ms HTTP status >> 200

【讨论】:

啊,我认为这是CORS docs 的“内容类型”。唯一允许的类型是表单或“文本/纯文本”的两种。由于它是一个 GET 请求,因此您不需要它,它是您发送的数据的内容类型。【参考方案3】:

不确定具体关于这个问题,但如果您在发出CORS 请求时遇到问题,请使用此chrome extension(允许控制允许来源)。

这将使您无需在headers/config 中传递任何额外参数即可发出CORS 请求。

这样做的缺点是,它不适用于CORS patch request

【讨论】:

以上是关于Angular 4 App调用Play Framework Web App的CORS问题的主要内容,如果未能解决你的问题,请参考以下文章

在集成测试 Play 2.4.X 中模拟外部 Web 服务调用

在@Input 上使用切片时调用了两次组件的设置器。角度 4

Google Play Store App 的 Apache-Cordova 更新到 4.1.1 或更高版本,但它的

Angular随笔第一课

AngularJs中的模块定义,一个页面加载多个ng-app

在Angular5 app中的leaflet弹出窗口中调用zone.run