为Angular 2的HTTP请求设置基本URL

53

我正在尝试为所有的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

9个回答

55

对于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 { }

编辑:HttpClient和HttpInterceptor在Angular 4.3中引入。


3
为了避免在拦截器中硬编码URL,可以通过以下方式受益于依赖注入:使用类似于constructor(@Inject('BASE_URL') private baseUrl: string) {}的代码。其中"BASE_URL"是一个标记依赖注入的标识符,该标识符与一个字符串(即URL)相关联,这样就可以在整个应用程序中更轻松地更改URL而不必更改拦截器的代码。 - kmos.w

18

更新:对于Angular 4,请参见@vegazz的答案。

对于Angular 2.2.1,以下内容应该作为Web API基础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 中注入:

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

使用示例:

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

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


2
这似乎是处理它的最清洁的方式。我对你的代码进行了细微的更改,并使用localStrategy.getBaseHref(),以便我可以将HTML中的base href设置为/dev,并且只有在那时才会执行代码。在我的情况下,我只需要在本地工作时重写此URL。 - tftd
1
只是想补充一下,我在Angular 4.3.0中测试过,它仍然像魔法一样运行良好。谢谢! - anderaus
1
不适用于Angular 4和HttpClientModule。但使用拦截器更简单。请参见下面的答案。 - vegazz
1
它仍然适用于Angular 6,但仅适用于HttpModule而不是HttpClientModule。 - Abel Valdez

11

针对 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覆盖。(在此处查看源代码)。

因此,在CustomRequestOptions合并方法的最后一步中,我生成了一个新的CustomRequestOptions实例,以确保以下操作仍然可以正常工作,而不是返回super.merge(...)

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 [as constructor]中...有任何想法吗? - sawe
你正在使用哪个版本的Angular?我在github上发现了这个新问题,我想知道它是否与该版本有关。 - yeelan
@angular/core@^2.3.1,我会查看你发布的链接并尝试获取相关信息。谢谢。 - sawe
@sawe,你的研究有什么进展吗?我正在使用angular@2.4.5angular-cli@1.0.0-beta.26 - sax
最终我创建了自己的HttpClient,它仅仅是利用了Angular Http,并添加了头信息等,另外还使用了我偏爱的API网址。 - sawe
显示剩余2条评论

7

不同的方法:在开发过程中在本地主机上运行您的应用程序时,考虑配置一个代理。

proxy.conf.json

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

More: link


3
需要更多人给这个投票赞成。上面的所有解决方案都只是因为一个小前缀而过于复杂。 - OzzyTheGiant
请记得使用 ng serve --proxy-config proxy.conf.json --open 命令启动服务器。 - Mike D3ViD Tyson
这对于 ng serve 来说没问题,但对于生产环境来说不行,因为它不是 ng build 的选项。 - Dodi
事实上,这种方法假设Angular应用程序将部署到相同的后端目标,因此在生产环境中不需要代理您的请求。 - István Békési
然而,如果您的Angular应用程序可在http://10.7.18.21:8080/上使用,同时http://10.7.18.21:8080/api是其后端,则完全没有问题。 - István Békési

6
在Angular 2.0.0-beta.6中,您可以通过覆盖“merge”来实现此目的。
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);
  }

}

4
刚刚发现它能处理 GETDELETE 请求,但无法处理 POSTPUT 请求。:/ - TheKojuEffect
正如@TheKojuEffect所提到的,这种解决方案对于使用更优雅API的某些方法不起作用。然而,如果您选择使用更冗长的一个:_http.request(..)_,它似乎能够工作。 - Adrian Sobiczewski
实际上,我发现这并不容易实现,因为super.merge实际上返回一个RequestOptions实例,在Http#post的情况下会创建一个新的RequestOptions实例,然后再与旧实例合并。长话短说,我真的不得不重写merge并返回一个自定义实例,就像这样:https://gist.github.com/rweng/1d36dd91842f3cb58ebb - rweng
它不适用于 http.posthttp.put,但适用于 http.request,因此您可以像这样使用:this.http.request('/user/1', { body: body, method: 'PUT', headers: headers }) - Tiberiu Popescu

5
在查看了 BaseRequestOptions, RequestOptionsHttp 类的代码源之后: 看起来,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本身 ;-)

希望对您有所帮助, Thierry


我将如何处理附加在我的URL中的查询字符串。 - Aniruddha Das

1

0

对于当前用户,这里有一个在angular 2.4.8中真正有效的示例:

这里是为什么要拆分和链接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);
}


0
创建一个 .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/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接