You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 08:05:24