【问题标题】:Set base url for angular 2 http requests为 Angular 2 http 请求设置基本 url
【发布时间】:2016-04-03 16:02:08
【问题描述】:

我正在尝试为我的所有 angular 2 http 请求设置基本 url。以下是我的应用程序的基本设置。

class HttpOptions extends BaseRequestOptions {
  url:string = "http://10.7.18.21:8080/api/";
}


bootstrap(AppComponent, [
  HTTP_PROVIDERS,
  provide(RequestOptions, {useClass: HttpOptions})
]);


export class AppComponent {
  users:Array<User>
  constructor(private http: Http) {
    http.get("/users")
      .subscribe(res => this.users = res.json());
  }
}

请求没有按照我的配置发送到http://10.7.18.21:8080/api/users。而是将请求发送到http://localhost:8000/users

如何在 Angular 2 应用程序中设置 http 请求的基本 url?

我正在使用 Angular 2.0.0-beta.0

【问题讨论】:

    标签: angular angular2-http


    【解决方案1】:

    对于 Angular 4.3+ 和 @angular/common/http

    这可以通过拦截器来完成

    @Injectable()
    export class ExampleInterceptor implements HttpInterceptor {
      intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const url = 'http://myurl.com';
        req = req.clone({
          url: url + req.url
        });
        return next.handle(req);
      }
    }
    

    app.module.ts

    import { NgModule } from '@angular/core';
    import { Injectable } from '@angular/core';
    import { HttpClientModule, HttpRequest, HTTP_INTERCEPTORS } from '@angular/common/http';
    
    @NgModule({
      declarations: [
        AppComponent,
        ...
      ],
      imports: [
        BrowserModule,
        HttpClientModule,
        ...
      ],
      providers: [
        AppComponent,
        { provide: HTTP_INTERCEPTORS, useClass: ExampleInterceptor, multi: true }
      ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule { }
    

    编辑:Angular 4.3 中引入了 HttpClient 和 HttpInterceptor

    【讨论】:

    • 注意:为了避免在拦截器中对 url 进行硬编码,可以通过执行以下操作从 DI 中受益:constructor(@Inject('BASE_URL') private baseUrl: string) {}
    【解决方案2】:

    更新:请参阅 @vegazz 对 Angular 4 的回答。

    对于 Angular 2.2.1,以下内容应作为 web api base url 的前缀并代表更小的占用空间:

    import {Request, XHRBackend, XHRConnection} from '@angular/http';
    
    @Injectable()
    export class ApiXHRBackend extends XHRBackend {
        createConnection(request: Request): XHRConnection {
            if (request.url.startsWith('/')){
                request.url = 'http://localhost:3000' + request.url;     // prefix base url
            }
            return super.createConnection(request);
        }
    }
    

    注入 app.module.ts 中的提供者:

    providers: [
        { provide: XHRBackend, useClass: ApiXHRBackend }
      ]
    

    使用示例:

    this._http.post('/api/my-route', {foo: 'bar'})
                .map<MyResponse>(res => res.json())
    

    它似乎涵盖了所有方法(GET、PUT、POST、...)

    【讨论】:

    • 这似乎是最干净的处理方式。我对您的代码稍作更改并使用localStrategy.getBaseHref(),以便我可以将HTML 中的base href 设置为/dev,然后代码才会执行​​。就我而言,当我在本地工作时,我只需要这个 url rewrite
    • 只是想补充一点,我在 Angular 4.3.0 中对此进行了测试,它仍然像魅力一样工作。谢谢!
    • 不适用于 Angular 4 和 HttpClientModule。但它更简单的拦截器。请参阅下面的答案。
    • 它仍在 Angular 6 中工作,但仅适用于 HttpModule 而不是 HttpClientModule
    【解决方案3】:

    对于 Angular2 版本 2.2(截至 2016 年 12 月)

    来自 RC5 的 Angular 将 HTTP_PROVIDERS 标记为已弃用并试图将内容移至 @NgModule,上述解决方案并不真正适用,因此他们的文档也是如此。我交叉引用了其他几个答案,并找到了实现基本 url 的方法,希望这对其他人有帮助。

    基本思想是,我们不是在引导程序中做事情,而是将事情转移到AppModule

    app.module.ts

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { HttpModule, RequestOptions } from '@angular/http';
    
    
    import { CustomRequestOptions } from './customrequest.options';
    
    @NgModule({
      declarations: [
        AppComponent,
        ...
      ],
      imports: [
        BrowserModule,
        HttpModule,
        ...
      ],
      providers: [
        { provide: RequestOptions, useClass: CustomRequestOptions }
      ],
      bootstrap: [ AppComponent ]
    })
    

    并将 CustomRequestOptions 移动到单独的可注入服务中

    import { Injectable } from '@angular/core';
    import { BaseRequestOptions, RequestOptions, RequestOptionsArgs } from '@angular/http';
    
    @Injectable()
    export class CustomRequestOptions extends BaseRequestOptions {
      merge(options?:RequestOptionsArgs):RequestOptions {
        options.url = 'http://localhost:9080' + options.url;
        return super.merge(options);
      }
    }
    

    编辑 GET 以外的请求方法。

    如果您尝试发送 GET 以外的请求类型,则之前的方法无法将 baseurl 注入请求中。这是因为 Angular2 生成了新的 RequestOptions 而不是 this._defaultOptions,其合并方法没有被我们的 CustomRequestOptions 覆盖。 (请参阅此处的source code)。

    因此,我没有在 CustomRequestOptions 合并方法的最后一步返回 super.merge(...),而是生成了一个新的 CustomRequestOptions 实例,以确保以下操作仍然有效。

    import { Injectable } from '@angular/core';
    import { RequestOptions, RequestOptionsArgs } from '@angular/http';
    
    @Injectable()
    export class CustomRequestOptions extends RequestOptions {
      merge(options?: RequestOptionsArgs): RequestOptions {
        if (options !== null && options.url !== null) {
          options.url = 'http://localhost:9080' + options.url;
        }
        let requestOptions = super.merge(options)
        return new CustomRequestOptions({
          method: requestOptions.method,
          url: requestOptions.url,
          search: requestOptions.search,
          headers: requestOptions.headers,
          body: requestOptions.body,
          withCredentials: requestOptions.withCredentials,
          responseType: requestOptions.responseType
        });
      }
    }
    

    这也适用于 POST、PUT、DELETE 方法。希望这会有所帮助。

    【讨论】:

    • 我收到一个错误:错误:无法解析 CustomRequestOptions 的所有参数:(?)。在 SyntaxError.BaseError [作为构造函数]...有什么想法吗?
    • 您在哪个版本的 Angular 上运行?我在github上找到了这个new issue,不知道是不是和版本有关。
    • @angular/core@^2.3.1,我会看看我能从你发布的那个链接中得到什么。 ta
    • @sawe 您的研究顺利吗?我正在使用 angular@2.4.5 和 angular-cli@1.0.0-beta.26。
    • 最终创建了我自己的 HttpClient,除了使用我喜欢的 api url 之外,它还简单地使用了 angular http 并添加了标头等:
    【解决方案4】:

    不同的方法:在 localhost 中运行应用程序的开发过程中,请考虑配置一个 proxy

    proxy.conf.json

    {
       "/api/**": {
          "target": "http://10.7.18.21:8080/",
          "secure": false,
          "logLevel": "debug"
       }
    }
    

    更多:link

    【讨论】:

    • 这需要更多的支持。上述所有解决方案都只是为了一个小前缀而过度设计。
    • 记得用ng serve --proxy-config proxy.conf.json --open启动服务器
    • 这对于 ng serve 很好,但它不适用于生产,因为它不是 ng build 的选项
    • 确实,这种方法假设 Angular 应用程序将部署到相同的后端目标,因此您无需在生产环境中代理您的请求。
    • 但是,如果您的 Angular 应用程序在 10.7.18.21:8080 可用,而 10.7.18.21:8080/api 是它的后端,那就太好了。
    【解决方案5】:

    Angular 2.0.0-beta.6 中,您可以通过覆盖“合并”来实现此目的

    import {BaseRequestOptions, RequestOptions, RequestOptionsArgs} from 'angular2/http';
    
    export class ApRequestOptions extends BaseRequestOptions {
    
      merge(options?:RequestOptionsArgs):RequestOptions {
        options.url = 'http://10.7.18.21:8080/api' + options.url;
        return super.merge(options);
      }
    
    }
    

    【讨论】:

    • 刚刚发现它适用于GETDELETE,但不适用于POSTPUT。 ://
    • 正如@TheKojuEffect 提到的,此解决方案不适用于某些使用更优雅 API 的方法。但是,如果您选择使用更详细的方法:http.request(..),它似乎可以工作。
    • 实际上,我发现实现这一点并不容易,因为 super.merge 实际上返回一个RequestOptions 实例,如果是Http#post,则在合并之前创建一个新的RequestOptions 实例旧的。长话短说,我真的不得不重写合并并返回一个这样的自定义实例:gist.github.com/rweng/1d36dd91842f3cb58ebb
    • 它不适用于 http.posthttp.put ,但它适用于 http.request ,所以你可以有类似的东西:this.http.request('/user/1', { body: body, method: 'PUT', headers: headers })
    【解决方案6】:

    查看BaseRequestOptionsRequestOptionsHttp 类的代码源后:

    url 属性似乎对应于默认 URL,但不是 URL 的前缀。

    为了实现您的用例,我建议在 http 对象前面放置一个服务,然后注入该服务。类似的东西:

    import {Injectable} from '@angular/core';
    import {Http} from '@angular/http';
    
    @Injectable()
    export class HttpClient {
      http: Http;
      urlPrefix: string;
    
      constructor(http: Http) {
        this.http = http;
        this.urlPrefix = 'http://...';
      }
    
      get(url) {
        return this.http.get(this.urlPrefix + url);
      }
    
      post(url, data) {
        return this.http.post(this.urlPrefix + url, data);
      }
    }
    

    import {HttpClient} from './http-client';
    
    export classMyComponent {
      constructor(httpClient: HttpClient) {
        this.httpClient = httpClient;
      }
    
      handleSomething() {
        this.httpClient.post(url, data)
        ).subscribe(...);
      }
    }
    

    也就是说,这可能是对 Angular2 本身的贡献;-)

    希望对你有帮助 蒂埃里

    【讨论】:

    • 我将如何处理附加在我的 url 中的查询字符串。
    【解决方案7】:
    import {LocationStrategy} from 'angular2/router';
    
    constructor(private locationStrategy:LocationStrategy) {
      console.log(locationStrategy.prepareExternalUrl('xxx'));
    }
    

    另见https://github.com/angular/angular/blob/1bec4f6c6135d7aaccec7492d70c36e1ceeaeefa/modules/angular2/test/router/path_location_strategy_spec.ts#L88

    【讨论】:

      【解决方案8】:

      对于当前用户,这是 angular 2.4.8

      中真正有效的示例

      here 是为什么拆分和链接 BaseCommonRequestOptionsCommonRequestOptions 的代码。

      import { BaseRequestOptions, Headers, RequestOptions, RequestOptionsArgs } from '@angular/http';
      import { Injectable } from '@angular/core';
      
      @Injectable()
      export class BaseCommonRequestOptions extends BaseRequestOptions {
      
        merge(options?: RequestOptionsArgs): RequestOptions {
          return new CommonRequestOptions(super.merge(extracted(options)));
        }
      }
      
      /**
       * for inner merge when using post put patch delete...others method
       */
      export class CommonRequestOptions extends RequestOptions {
        merge(options?: RequestOptionsArgs): RequestOptions {
          return new RequestOptions(super.merge(extracted(options)));
        }
      }
      
      /**
       * inject default values
       *
       * @param options
       * @returns {RequestOptionsArgs}
       */
      export function extracted(options: RequestOptionsArgs) {
        console.log(options);
        if (!validUrl(options.url)) {
          options.url = 'http://localhost:3000' + (options.url ? options.url : "");
        }
        // use default header application/json, if content-type header was empty.
        if (options.headers != null) {
          let contentType = options.headers.get('content-type');
          if (contentType == null || contentType == '') {
            options.headers.append('content-type', 'application/json');
          }
        } else {
          options.headers = new Headers({ 'content-type': 'application/json' });
        }
      
        return options;
      }
      
      /**
       * validate url
       *
       * @param url
       * @returns {boolean}
       */
      export function validUrl(url: string) {
        return /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/.test(url);
      }

      【讨论】:

        【解决方案9】:

        创建一个 .ts 文件

        import { Injectable } from '@angular/core';
        import {Request, XHRBackend, XHRConnection} from '@angular/http';
        
        
            @Injectable()
            export class ApiXHRBackend extends XHRBackend {
            createConnection(request: Request): XHRConnection {
            if (request.url.startsWith('/api')){
            var url=request.url.replace("/api", "");
            request.url = 'http://localhost:8080' + url; // prefix base url
            }
            return super.createConnection(request);
            }
        
        }
        

        然后在 app.module.ts 中

        import { ApiXHRBackend } from './guard/httpintercepter';
        import {Request, XHRBackend, XHRConnection} from '@angular/http';
        

        在提供者部分添加提供者

        providers: [
        {provide: XHRBackend, useClass: ApiXHRBackend }
        ],
        

        那么在你的 service.ts http 调用中必须像下面的例子

        return this._http.post("/api/customerservice/loginvalidation",data)
        .map((response: Response) => {
        return response;
        });
        

        这里的 /api 将替换为您的基本 URL http://localhost:8080

        更多细节 http://shabeen.in/webschool/how-to-set-angular-2-service-http-base-url/

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-05-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多