在 Angular 2 http.post 上添加任何选项或标题会发送 OPTIONS

Posted

技术标签:

【中文标题】在 Angular 2 http.post 上添加任何选项或标题会发送 OPTIONS【英文标题】:Adding any options or headers on the Angular 2 http.post sends OPTIONS 【发布时间】:2017-06-22 11:54:29 【问题描述】:

我正在尝试通过http.post() 将令牌信息发送回服务器。如果我从中删除选项,它会发送一个 POST,但如果我将它们添加回来,它会发送被服务器代码拒绝的 OPTIONS。我也尝试删除“withCredentials”。

export class EntityService 

    public entity: EntityModel;
    private options: RequestOptions;

    constructor( @Inject(Http) private http: Http, @Inject(AuthenticationService) authService) 
        let headers = new Headers( 'X-Authorization': 'Bearer ' + authService.token);
        this.options = new RequestOptions( headers: headers, withCredentials: true );
    

    public store(entity: EntityModel): Observable<string> 

        var request;
        if (!entity.uuid) 
            request = this.http.post("http://localhost:8080/api/entity", JSON.stringify(entity), this.options);
        
        else 
            request = this.http.put("http://localhost:8080/api/entity", JSON.stringify(fact), this.options);
        
        return request.map((res: Response) => res.text());
    

我的身份验证服务如下所示:

import  Injectable, Inject  from '@angular/core';
import  Http, Headers, Response  from '@angular/http';
import  Observable  from 'rxjs';
import 'rxjs/add/operator/map'

//http://jasonwatmore.com/post/2016/08/16/angular-2-jwt-authentication-example-tutorial
@Injectable()
export class AuthenticationService 
    public token: string;

    constructor(@Inject(Http) private http: Http) 
        // set token if saved in local storage
        var currentUser = JSON.parse(localStorage.getItem('currentUser'));
        this.token = currentUser && currentUser.token;
    

    login(username: string, password: string): Observable<boolean> ;
        console.log("login...");
        return this.http.post('http://localhost:8080/api/auth/login', JSON.stringify( username: username, password: password ))
            .map((response: Response) => 
                // login successful if there's a jwt token in the response
                let token = response.json() && response.json().token;
                if (token) 
                    // set token property
                    this.token = token;

                    // store username and jwt token in local storage to keep user logged in between page refreshes
                    localStorage.setItem('currentUser', JSON.stringify( username: username, token: token ));

                    // return true to indicate successful login
                    return true;
                 else 
                    // return false to indicate failed login
                    return false;
                
            );
    

    logout(): void 
        // clear token remove user from local storage to log user out
        this.token = null;
        localStorage.removeItem('currentUser');
    

这是我的 Spring 配置:

@SpringBootApplication
public class SpringBootApp extends WebMvcConfigurerAdapter 

    private boolean workOffline = true;
    private boolean setupSchema = false;
    private IGraphService graphService;
    private DbC conf;

    @Autowired
    public SpringBootApp(IGraphService graphService, DbC conf)
    
        this.graphService = graphService;
        this.conf = conf;
    

    public static void main(String[] args) throws Exception 
        SpringApplication.run(SpringBootApp.class, args);
    

    @Bean
    public Filter caseInsensitiveRequestFilter() 
        return new CaseInsensitiveRequestFilter();
    

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) 
        configurer.enable();
    

    @Override
    public void addCorsMappings(CorsRegistry registry) 
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "PUT", "POST", "DELETE","OPTIONS");
    

    @Bean
    public FilterRegistrationBean corsFilter() 
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:3000");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    

我真的不知道该怎么做,因为我正在关注Angular2 OPTIONS method sent when asking for http.GET 中所说的内容,这不是预检请求。我之前遇到过这个问题,内容类型错误。

【问题讨论】:

不需要评论反对票。此外,downvote 并不一定意味着重复 - 例如,一些重复实际上是有用的。 【参考方案1】:

OPTIONS 请求仅由浏览器发出。 Angular 根本不涉及。

“这不是预检请求。” - 绝对是。

您需要配置您的服务器以正确响应OPTIONS 请求,或者确保从您发出请求的同一服务器(也是同一端口)加载 Angular 应用程序。

【讨论】:

谢谢甘特!但是我还没有找到一种方法,可以在不让浏览器发送 OPTIONS 请求的情况下发送带有令牌信息的自定义标头。当有人发送 OPTIONS 而不是 POST 时我应该做什么。我什么都不返回。在什么情况下浏览器会在 OPTIONS 之后发送 POST?我已将服务器配置为响应 OPTIONS,但不幸的是,我不能将这两个端口放在同一个端口上,因为一个是 node.js,一个是码头 restlet.com/blog/2015/12/15/understanding-and-using-cors "POST 方法,仅支持具有以下值的内容类型:text/plain、application/x-www-form-urlencoded 和 multipart/form-data。" withCredentials 也会导致 OPTIONS 预检请求。 服务器需要返回CORS头AccessControlAllowXxx 查看spring.io/blog/2015/06/08/cors-support-in-spring-framework - 搜索“allowCredentials” - 在您的情况下需要设置为true 如果浏览器在对OPTIONS 请求的响应中获得正确的标头,则在OPTIONS 之后发送POST。如果没有,您会在浏览器控制台中收到错误消息,而不是告诉您缺少哪些标头。【参考方案2】:

实际修复有两个原因: 不正确的 CORS 实施 - 更多信息请看这里:Spring 4/5 global CORS configuration doesn't work giving `No 'Access-Control-Allow-Origin' header is present on the requested resource`

然后当我在登录后发布时,我收到错误415 Unsupported Media Type。按照此处的说明操作后:POST JSON fails with 415 Unsupported media type, Spring 3 mvc

我在请求中添加了Content-TypeAccept 标头,它解决了问题。看来Content-Type 才是真正需要的。

export class EntityService 

    public entity: EntityModel;
    private options: RequestOptions;

    constructor( @Inject(Http) private http: Http, @Inject(AuthenticationService) authService) 
        let headers = new Headers( 
           'X-Authorization': 'Bearer ' + authService.token,
           'Content-Type': 'application/json'
        );
        this.options = new RequestOptions( headers: headers, withCredentials: true );
    

    public store(entity: EntityModel): Observable<string> 

        var request;
        if (!entity.uuid) 
            request = this.http.post("http://localhost:8080/api/entity", JSON.stringify(entity), this.options);
        
        else 
            request = this.http.put("http://localhost:8080/api/entity", JSON.stringify(fact), this.options);
        
        return request.map((res: Response) => res.text());
    

【讨论】:

【参考方案3】:

像这样使用http post

import  Http, Headers, Response, Request  from '@angular/http';

let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('X-Authorization', this.token);
headers.append('Authorization', 'Bearer ' + jwtToken);

return this.http.post(url, data, headers)
  .map(res =>  console.log(res) )
  .catch(err =>  console.log(err)  );

请注意,此示例返回一个您可以订阅的 Observable。也是我的例子

【讨论】:

以上是关于在 Angular 2 http.post 上添加任何选项或标题会发送 OPTIONS的主要内容,如果未能解决你的问题,请参考以下文章

Angular2 http post请求到另一个端口上的nodejs服务器

ionic 2 angular 2 从 ion-select 中获取值以发送 http post

Angular http post 仅在 Chrome 中在服务器上返回 403 错误

Spring Boot + Angular 5 - http.post 空值

angular2 http.post 方法抛出 typeerror 异常

Angular2 http post没有将json对象传递给MVC 6控制器动作