将工作 Web 应用程序转变为适用于 Android 的本机 Ionic 应用程序时出现 CORS 问题

Posted

技术标签:

【中文标题】将工作 Web 应用程序转变为适用于 Android 的本机 Ionic 应用程序时出现 CORS 问题【英文标题】:CORS issue while shaping working web app into a native Ionic app for Android 【发布时间】:2020-12-23 20:04:06 【问题描述】:

我一直试图在guide 之后设置这个 Ionic CLI 代理服务器,但它是从 2015 年开始的,我不知道如何在 Angular 10 中实现它。

所以当我使用命令运行我的应用程序时:

ionic capacitor run android --project=myApp -c=production

我在 Android Studio 中有这个错误:

E/Capacitor/Console: File: http://localhost/login - Line 0 - Msg: Access to XMLHttpRequest at 'https://remoteServer.com/api/v1/oauth/v2/token' from origin 'http://localhost' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' head

这是我的 capacitor.config.json 文件:


  "appId": "io.ionic.starter",
  "appName": "myApp",
  "bundledWebRuntime": false,
  "npmClient": "npm",
  "webDir": "www",
  "plugins": 
    "SplashScreen": 
      "launchShowDuration": 0
    
  ,
  "cordova": ,
  "linuxAndroidStudioPath": "/opt/android-studio/bin/studio.sh"

这是我的 ionic.config.json 文件:


  "name": "myApp",
  "integrations": 
    "capacitor": 
  ,
  "type": "angular",
  "proxies": [
    
      "path": "/api",
      "proxyUrl": "https://remoteServer.com/api"
    
  ]

离子信息

Ionic:

   Ionic CLI                     : 6.10.1 (/home/user/.nvm/versions/node/v12.18.3/lib/node_modules/@ionic/cli)
   Ionic Framework               : @ionic/angular 5.3.1
   @angular-devkit/build-angular : 0.1000.5
   @angular-devkit/schematics    : 10.0.5
   @angular/cli                  : 10.0.5
   @ionic/angular-toolkit        : 2.3.3

Capacitor:

   Capacitor CLI   : 2.4.0
   @capacitor/core : 2.4.0

Utility:

   cordova-res : not installed
   native-run  : not installed

System:

   NodeJS : v12.18.3 (/home/user/.nvm/versions/node/v12.18.3/bin/node)
   npm    : 6.14.6
   OS     : Linux 5.4

任何想法如何解决这个问题?我一直在寻找年龄......


编辑:

所以我按照 Angular 在 how to make an interceptor 和这篇解释 how to implement both HttpClient and Ionic's native HTTP 的文章中的说明进行操作,但遇到了新问题。

使用文章中的代码,TS 抱怨这一行:

headers: nativeHttpResponse.headers

(property) headers?: HttpHeaders
Type ' [key: string]: string; ' is missing the following properties from type 'HttpHeaders': headers, normalizedNames, lazyInit, lazyUpdate, and 12 more.ts(2740)
http.d.ts(3406, 9): The expected type comes from property 'headers' which is declared here on type ' body?: any; headers?: HttpHeaders; status?: number; statusText?: string; url?: string; '

这是整个 native-http.interceptor.ts:

import  Injectable  from "@angular/core";
import 
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpResponse,
 from "@angular/common/http";
import  Observable, from  from "rxjs";
import  Platform  from "@ionic/angular";
import  HTTP  from "@ionic-native/http/ngx";

type HttpMethod =
  | "get"
  | "post"
  | "put"
  | "patch"
  | "head"
  | "delete"
  | "upload"
  | "download";

@Injectable()
export class NativeHttpInterceptor implements HttpInterceptor 
  constructor(private nativeHttp: HTTP, private platform: Platform) 

  public intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> 
    if (!this.platform.is("cordova")) 
      return next.handle(request);
    

    return from(this.handleNativeRequest(request));
  

  private async handleNativeRequest(
    request: HttpRequest<any>
  ): Promise<HttpResponse<any>> 
    const headerKeys = request.headers.keys();
    const headers = ;

    headerKeys.forEach((key) => 
      headers[key] = request.headers.get(key);
    );

    try 
      await this.platform.ready();

      const method = <HttpMethod>request.method.toLowerCase();

      // console.log(‘— Request url’);
      // console.log(request.url)
      // console.log(‘— Request body’);
      // console.log(request.body);

      const nativeHttpResponse = await this.nativeHttp.sendRequest(
        request.url,
        
          method: method,
          data: request.body,
          headers: headers,
          serializer: "json",
        
      );

      let body;

      try 
        body = JSON.parse(nativeHttpResponse.data);
       catch (error) 
        body =  response: nativeHttpResponse.data ;
      

      const response = new HttpResponse(
        body: body,
        status: nativeHttpResponse.status,
        headers: nativeHttpResponse.headers,  <--------
        url: nativeHttpResponse.url,
      );

      // console.log(‘— Response success’)
      // console.log(response);

      return Promise.resolve(response);
     catch (error) 
      if (!error.status) 
        return Promise.reject(error);
      

      const response = new HttpResponse(
        body: JSON.parse(error.error),
        status: error.status,
        headers: error.headers,
        url: error.url,
      );

      return Promise.reject(response);
    
  

这是我的app.module.ts 的样子:

import  BrowserModule  from '@angular/platform-browser';
import  NgModule  from '@angular/core';
import  RouterModule  from '@angular/router';
import  FormsModule, ReactiveFormsModule  from '@angular/forms';
import  BrowserAnimationsModule  from '@angular/platform-browser/animations';
import  IonicModule  from '@ionic/angular';
import  HTTP  from '@ionic-native/http/ngx';

import  CoreModule  from './core/core.module';
import  SharedModule  from './shared/shared.module';
import  AppComponent  from './app.component';
import  PageNotFoundComponent  from './shared/page-not-found/page-not-found.component';
import  appRoutes  from './app.routes';


@NgModule(
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    ReactiveFormsModule,
    SharedModule,
    CoreModule,
    RouterModule.forRoot(
      appRoutes
    ),
    IonicModule.forRoot()
  ],
  providers: [HTTP],
  declarations: [
    AppComponent,
    PageNotFoundComponent
  ],
  bootstrap: [AppComponent]
)
export class AppModule  

这是我的core.module.ts(我想在其中使用拦截器)的样子:

import  NgModule  from "@angular/core";
import  CommonModule  from "@angular/common";
import  HTTP_INTERCEPTORS, HttpClientModule  from "@angular/common/http";

import  NativeHttpInterceptor  from "./service/native-http.interceptor";
import  AuthService  from "./service/auth.service";
import  ApiService  from "./service/api.service";
import  AuthGuardService  from "./service/auth-guard.service";
import  AuthInterceptor  from "./service/auth-interceptor";
import  WindowRef  from "./service/window-ref-service";

@NgModule(
  imports: [CommonModule, HttpClientModule],
  providers: [
    
      provide: HTTP_INTERCEPTORS,
      useClass: NativeHttpInterceptor,
      multi: true,
    ,
    AuthService,
    ApiService,
    AuthGuardService,
    WindowRef,
    
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true,
    ,
  ],
)
export class CoreModule 

【问题讨论】:

您是否有权访问服务器以应用一些配置? 这里是 CORS 的更新 Ionic 文章:ionicframework.com/docs/troubleshooting/cors @yazantahhan 不,我无权更改 BE 上的任何内容。感谢您提供链接。 你能把抱怨标题的代码块放上去吗? @yazantahhan 添加了 TS 错误 + native-http.interceptor.ts & app.module.ts & core.module.ts 【参考方案1】:

代理配置仅适用于原生构建的 ionic servelivereload

如果您无法更改 BE 中的任何选项,那么最简单的方法是对 HTTP 请求使用本机插件cordova-plugin-advanced-http,它将发送不带原始标头的请求(因为它不是从浏览器发送的)。

您可以使用来自 here 的 Ionic Native 包装器。

【讨论】:

您是否知道我能否以及如何相对轻松地用这个 HTTP 插件替换我的 HttpClient?我在 api.service 中有数百行代码需要修改... 你可以有一个拦截器来为你做这件事。创建一个HTTP interceptor,它将捕获所有请求和响应,然后您将使用插件而不是 HttpClient 来发送请求。它需要一些工作,但这是最好的方法,因此您不必更改所有其他地方。您的所有逻辑都将集中在一个地方 你可以查看这个:manuel-heidrich.dev/blog/… 是否有一个具有此实现的示例项目?我不能让它工作...... TS 在这一行抱怨“headers”类型: headers: nativeHttpResponse.headers

以上是关于将工作 Web 应用程序转变为适用于 Android 的本机 Ionic 应用程序时出现 CORS 问题的主要内容,如果未能解决你的问题,请参考以下文章

将应用程序限制为移动设备[重复]

使用本地系统应用程序池标识时,进程仅适用于 Web 应用程序

适用于 Windows 8 应用商店 Web 资源的 Worklight 加密

为什么我的字符集编码转换仅适用于小写字母?

自定义适用于手机和平板电脑的 Dynamics 365:窗体自定义项

关于 CORS,为啥我部署的 Azure 应用程序适用于除一个用户之外的所有人?