Angular请求Kotlin Spring Boot后端失败(Postman可正常调用)
问题:Angular请求Spring Boot后端失败(Postman正常)
从Angular发送POST请求到Kotlin Spring Boot后端时失败,请求在Chrome控制台显示为canceled,无CORS错误,但Postman调用完全正常。无token时会返回401,说明token有效。
Angular请求代码
const data = {test: "test"} const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json,, 'Authorization': 'Bearer ' + localStorage.getItem(environment.STORAGE_KEYS.TOKEN) }') }; this.http.post("http://localhost:1234/test", data, httpOptions) .subscribe( response => { console.log("sucesss"); console.log(response); }, error => { console.log("error"); console.error(error); } );
Spring Boot相关代码
Controller
@RestController class TestController { @RequestMapping( method = [RequestMethod.POST], value = ["/test"], produces = ["application/json"], consumes = ["application/json"] ) fun test(){ println("test"); } }
Security Config
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) class SecurityConfig(private val issuersOAuth2Properties: IssuersOAuth2Properties) { @Bean @Throws(Exception::class) fun filterChain(http: HttpSecurity): SecurityFilterChain? { http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll().and().authorizeRequests() .antMatchers("/**").authenticated().and().oauth2ResourceServer{oauth2 -> oauth2.authenticationManagerResolver(jwtIssuerAuthManager()) }.authorizeRequests() return http.build() } @Bean fun jwtIssuerAuthManager(): AuthenticationManagerResolver<HttpServletRequest?>?{ return JwtIssuerAuthenticationManagerResolver(IssuerManager(issuersOAuth2Properties.issuers)) } }
Cors Config
@EnableWebMvc @Configuration class CorsConfig: WebMvcConfigurer { override fun addCorsMappings(registry: CorsRegistry) { logger().info("this is cors here") registry.addMapping("/**").allowedOrigins("*").allowedMethods("*").allowedHeaders("*") } }
复制的Chrome网络请求(curl)
curl 'http://localhost:1234/test' \ -H 'Accept: application/json, text/plain, */*' \ -H 'Referer: http://localhost:8100/' \ -H 'Authorization: Bearer eyJhbGciO.....Q3Oz_xw' \ -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36' \ -H 'Content-Type: application/json' \ --data-raw '{"test":"test"}' \ --compressed
控制台栈追踪
scheduleTask @ http://localhost:8100/polyfills.js:11755 _ZoneDelegate.scheduleTask @ http://localhost:8100/polyfills.js:8281 onScheduleTask @ http://localhost:8100/polyfills.js:8174 _ZoneDelegate.scheduleTask @ http://localhost:8100/polyfills.js:8276 Zone.scheduleTask @ http://localhost:8100/polyfills.js:8091 Zone.scheduleMacroTask @ http://localhost:8100/polyfills.js:8120 scheduleMacroTaskWithCurrentZone @ http://localhost:8100/polyfills.js:8685 (anonymous) @ http://localhost:8100/polyfills.js:11800 proto.<computed> @ http://localhost:8100/polyfills.js:9042 (anonymous) @ http://localhost:8100/vendor.js:44103 _trySubscribe @ http://localhost:8100/vendor.js:26437 subscribe @ http://localhost:8100/vendor.js:26419 innerSubscribe @ http://localhost:8100/vendor.js:27435 _innerSub @ http://localhost:8100/vendor.js:28800 _tryNext @ http://localhost:8100/vendor.js:28793 _next @ http://localhost:8100/vendor.js:28774 next @ http://localhost:8100/vendor.js:26906 (anonymous) @ http://localhost:8100/vendor.js:30308 _trySubscribe @ http://localhost:8100/vendor.js:26437 subscribe @ http://localhost:8100/vendor.js:26419 call @ http://localhost:8100/vendor.js:28757 subscribe @ http://localhost:8100/vendor.js:26417 call @ http://localhost:8100/vendor.js:28466 subscribe @ http://localhost:8100/vendor.js:26417 call @ http://localhost:8100/vendor.js:28628 subscribe @ http://localhost:8100/vendor.js:26417 (anonymous) @ http://localhost:8100/src_app_business-owner_business_add-business_add-business_module_ts.js:158 asyncGeneratorStep @ http://localhost:8100/vendor.js:129742 _next @ http://localhost:8100/vendor.js:129764 (anonymous) @ http://localhost:8100/vendor.js:129771 ZoneAwarePromise @ http://localhost:8100/polyfills.js:9436 (anonymous) @ http://localhost:8100/vendor.js:129760 addBusiness @ http://localhost:8100/src_app_business-owner_business_add-business_add-business_module_ts.js:211 AddBusinessPage_form_6_Template_ion_button_click_29_listener @ ng:///AddBusinessPage.js:96 executeListenerWithErrorHandling @ http://localhost:8100/vendor.js:91120 wrapListenerIn_markDirtyAndPreventDefault @ http://localhost:8100/vendor.js:91158 (anonymous) @ http://localhost:8100/vendor.js:118042 _ZoneDelegate.invokeTask @ http://localhost:8100/polyfills.js:8293 onInvokeTask @ http://localhost:8100/vendor.js:103217 _ZoneDelegate.invokeTask @ http://localhost:8100/polyfills.js:8293 Zone.runTask @ http://localhost:8100/polyfills.js:8043 ZoneTask.invokeTask @ http://localhost:8100/polyfills.js:8388 invokeTask @ http://localhost:8100/polyfills.js:9906 globalCallback @ http://localhost:8100/polyfills.js:9943 globalZoneAwareCallback @ http://localhost:8100/polyfills.js:9982
问题原因及解决方法
1. Angular请求头语法错误
Angular代码中HttpHeaders的配置存在语法错误:'Content-Type': 'application/json,,多了一个逗号和引号,导致请求头构造异常,直接引发请求被取消。
修正后的代码:
const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem(environment.STORAGE_KEYS.TOKEN) }) };
2. Spring Boot控制器未返回合法响应
控制器test()方法仅打印日志,未返回任何内容,但@RequestMapping标注了produces = ["application/json"],后端尝试生成JSON响应却无数据,导致响应异常,进而触发前端请求取消。
修正后的控制器代码:
@RequestMapping( method = [RequestMethod.POST], value = ["/test"], produces = ["application/json"], consumes = ["application/json"] ) fun test(): ResponseEntity<Map<String, String>>{ println("test"); return ResponseEntity.ok(mapOf("status" to "success")) }
3. Spring Security CORS配置优先级问题
Spring Security的CORS配置优先级高于WebMvc的配置,当前Security Config仅允许预检请求,未完整启用CORS处理,可能导致实际请求的跨域校验异常。
修正后的Security Config代码:
@Bean @Throws(Exception::class) fun filterChain(http: HttpSecurity): SecurityFilterChain? { http .cors().and() // 启用CORS,复用WebMvc的CORS配置 .authorizeRequests() .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() .antMatchers("/**").authenticated() .and() .oauth2ResourceServer{oauth2 -> oauth2.authenticationManagerResolver(jwtIssuerAuthManager()) } return http.build() }
内容的提问来源于stack exchange,提问作者mistermm
相关产品推荐
相关产品推荐

