如何在angular 4应用程序中存储api的根url?

如何在angular 4应用程序中存储api的根url?,angular,Angular,我遵循官方教程并为Api提供服务,但Api的绝对url在服务中是硬编码的 我希望将Api的基本url保留在某个位置,以便在调用之前将其附加到每个服务的url路径中。我还需要在构建之前(或构建之后)更改Api的基本url 我试着把它放在sessionstorage中,但这是个坏主意,因为任何人都可以更改它,我的应用程序将开始访问其他域 所以我对它进行了硬编码,并在git上放置了一个post推钩子,以在构建后替换url。但这更像是一种黑客手段,而不是解决方案 我可以在angular的根目录中放置一个

我遵循官方教程并为Api提供服务,但Api的绝对url在服务中是硬编码的

我希望将Api的基本url保留在某个位置,以便在调用之前将其附加到每个服务的url路径中。我还需要在构建之前(或构建之后)更改Api的基本url

我试着把它放在sessionstorage中,但这是个坏主意,因为任何人都可以更改它,我的应用程序将开始访问其他域

所以我对它进行了硬编码,并在git上放置了一个post推钩子,以在构建后替换url。但这更像是一种黑客手段,而不是解决方案

我可以在angular的根目录中放置一个文件,并将Api url设置为json格式。并将其包含在每个服务中,以便我可以从git中排除该文件,并且每个队友和构建服务器都可以拥有自己的具有不同url的文件


推荐的方法是什么?

通常我会将它们放在const环境文件中。如果您使用的是angular cli,则已经为您提供了该命令。如果没有,您可以创建自己的命令:

export const environment = {  
  production: false,
  api: 'http://localhost:4200/api/'
};
您可以有多个环境文件,如environment.production.ts,然后使用angular cli运行:

ng build --environment=production
如果您不使用angular cli,则可以非常肯定地构建自己的类似功能

export class AppSettings {
   public static API_ENDPOINT='http://127.0.0.1:6666/api/';
}
然后在服务中:

import {Http} from 'angular2/http';
import {Message} from '../models/message';
import {Injectable} from 'angular2/core';
import {Observable} from 'rxjs/Observable';
import {AppSettings} from '../appSettings';
import 'rxjs/add/operator/map';

@Injectable()
export class MessageService {

    constructor(private http: Http) { }

    getMessages(): Observable<Message[]> {
        return this.http.get(AppSettings.API_ENDPOINT+'/messages')
            .map(response => response.json())
            .map((messages: Object[]) => {
                return messages.map(message => this.parseData(message));
            });
    }

    private parseData(data): Message {
        return new Message(data);
    }
}
从'angular2/Http'导入{Http};
从“../models/Message”导入{Message};
从'angular2/core'导入{Injectable};
从“rxjs/Observable”导入{Observable};
从“../AppSettings”导入{AppSettings};
导入'rxjs/add/operator/map';
@可注射()
导出类消息服务{
构造函数(私有http:http){}
getMessages():可观察{
返回此.http.get(AppSettings.API_ENDPOINT+/messages)
.map(response=>response.json())
.map((消息:Object[])=>{
returnmessages.map(message=>this.parseData(message));
});
}
私有数据(data):消息{
返回新消息(数据);
}
}

应用程序模块中
添加窗口对象

import {provide} from 'angular2/core';
bootstrap([provide(Window, {useValue: window})]);
之后,您可以在控制器中访问
窗口

constructor(private window: Window) {
 var hostname = this.window.location.hostname;
}
没有特定于angularJS的解决方案。在angularJS(版本1)中,我们使用
$location
服务

$location.protocol() + "://" + $location.host() + ":" + $location.port();

使用环境文件

如果使用cli,则应使用环境文件。通过配置angular-cli.json文件,您可以管理所有可用环境并创建新环境。例如,您可以创建一个environment.dev.js文件并将值存储在那里,通过让git忽略它,您团队中的任何成员都可以拥有一个定制的文件

所述环境文件将覆盖原始environment.js


看到这个答案,Angular 4.3发布后,我们有可能使用HttpClient Interceptors。 这种方法的优点是避免了API的导入/注入。URL是所有带有API调用的服务

这是我的基本实现:

@Injectable()
export class ApiUrlInterceptor implements HttpInterceptor {
constructor(@Inject(API_URL) private apiUrl: string) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = req.clone({url: this.prepareUrl(req.url)});
    return next.handle(req);
  }

  private isAbsoluteUrl(url: string): boolean {
    const absolutePattern = /^https?:\/\//i;
    return absolutePattern.test(url);
  }

  private prepareUrl(url: string): string {
    url = this.isAbsoluteUrl(url) ? url : this.apiUrl + '/' + url;
    return url.replace(/([^:]\/)\/+/g, '$1');
  }
}
环境技术服务:

export const environment = {
 production: false,
 apiUrl: 'some-dev-api'
};

您可以创建一个拦截器来添加基本API URL

@Injectable()
export class CustomHttpInterceptor implements HttpInterceptor {

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.log('Custom Interceptor');

    // Adding serverHostURL to all APIs in Interceptor
    const serverHostURL = 'http://localhost:8080';
    request = request.clone({
      url: serverHostURL + request.url
    });


    return next.handle(request);
  }
@Injectable()
导出类CustomHttpInterceptor实现HttpInterceptor{
拦截(请求:HttpRequest,下一步:HttpHandler):可观察{
log('custominterceptor');
//将serverHostURL添加到侦听器中的所有API
const serverHostURL='1〕http://localhost:8080';
request=request.clone({
url:serverHostURL+request.url
});
下一步返回。处理(请求);
}

上面的代码应该可以。

从何处存储角度配置

因为这是一个经常出现的问题,因为它经常被错误地执行,而且还有一个很好的替代方案,所以今天我想讨论在哪里存储角度配置。您知道,当您从本地开发环境移动到开发、QA和生产服务器时,所有这些信息都会发生变化吗

这有个地方!

错误的位置 我知道这很诱人,但是environment.ts和environment.prod.ts文件从来都不是用来提供配置信息的,只是用来告诉运行时您正在运行生产版本的代码,而不是在本地开发代码。是的,我知道可以为您的不同环境创建一个文件,并且您可以有效地使用他会为你的配置信息创建一个文件。但是,仅仅因为你可以,并不意味着你应该这样做

在理想的情况下,您将构建一个候选版本并将其放置在开发服务器上,然后将其从开发服务器移动到QA,然后再移动到生产服务器。您永远不会重建应用程序。您希望绝对确保您在开发环境中测试的代码是您在QA环境中运行的代码,并且您所测试的代码是QA环境中的错误是在生产环境中运行的代码。您希望确定某些东西不工作的唯一可能原因是配置信息不正确

还有其他方法可以降低风险,但它们都涉及到重新编译代码和标记存储库,以便您可以获取相同的代码。这在没有更好的方法时有效。但是,有更好的方法

改为在哪里? 如果我们不能将配置信息放在代码中,我们应该把它放在哪里?显然是放在代码的外部。这给我们留下了几个解决方案。一个是创建一个静态json文件,当代码部署到每个环境中时,它会被复制到dist目录中。我看到的另一个工作是将代码放在数据库中数据库方法的优点是,您可以拥有一个数据库来处理所有应用程序甚至所有环境的配置信息。在其上放置一个良好的管理GUI,您可以轻松更改配置,而无需部署任何文件

跨栏 现在
export const environment = {
 production: false,
 apiUrl: 'some-dev-api'
};
@Injectable()
export class CustomHttpInterceptor implements HttpInterceptor {

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.log('Custom Interceptor');

    // Adding serverHostURL to all APIs in Interceptor
    const serverHostURL = 'http://localhost:8080';
    request = request.clone({
      url: serverHostURL + request.url
    });


    return next.handle(request);
  }
import { APP_INITIALIZER } from '@angular/core'

@NgModule({
    ....
    providers: [
        ...
        {
            provide: APP_INITIALIZER,
            useFactory: load,
            multi: true
        }
    ]
)
{
    "baseUrl": "https://davembush.github.io/api"
}
import { APP_INITIALIZER } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

function load() {

}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [{
    provide: APP_INITIALIZER,
    useFactory: load,
    multi: true
  }],
  bootstrap: [AppComponent]
})
export class AppModule { }
import { APP_INITIALIZER } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HttpClient } from '@angular/common/http';

import { AppComponent } from './app.component';
import { ConfigService } from './config.service';
import { of, Observable, ObservableInput } from '../../node_modules/rxjs';
import { map, catchError } from 'rxjs/operators';

function load(http: HttpClient, config: ConfigService): (() => Promise<boolean>) {
  return (): Promise<boolean> => {
    return new Promise<boolean>((resolve: (a: boolean) => void): void => {
       http.get('./config.json')
         .pipe(
           map((x: ConfigService) => {
             config.baseUrl = x.baseUrl;
             resolve(true);
           }),
           catchError((x: { status: number }, caught: Observable<void>): ObservableInput<{}> => {
             if (x.status !== 404) {
               resolve(false);
             }
             config.baseUrl = 'http://localhost:8080/api';
             resolve(true);
             return of({});
           })
         ).subscribe();
    });
  };
}
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [{
      provide: APP_INITIALIZER,
      useFactory: load,
      deps: [
        HttpClient,
        ConfigService
      ],
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }