常识来了
白蓝主题五 · 清爽阅读
首页  > 软件进阶

MVC跨域问题处理:前后端分离开发中的常见坑

做前后端分离项目时,前端跑在 localhost:3000,后端接口在 localhost:8080,一发请求就报错:‘No 'Access-Control-Allow-Origin' header’。这个提示太熟悉了,其实就是典型的跨域问题。在 MVC 架构的后端服务中,尤其是 ASP.NET MVC 或 Spring MVC 这类框架,这类问题天天见。

为什么会出现跨域限制?

浏览器出于安全考虑,实施了同源策略。只要协议、域名、端口有任何一个不同,就算跨域。比如前端是 http://localhost:3000,后端是 http://localhost:8080,虽然都在本机,但端口不同,照样被拦下。

在 MVC 中启用 CORS 最直接

以 ASP.NET MVC 为例,最常用的解法是在 Web.config 或代码中配置 CORS(跨域资源共享)。先装 NuGet 包 Microsoft.AspNet.WebApi.Cors,然后在 App_Start 文件夹下的 WebApiConfig.cs 中加入:

config.EnableCors();

接着在控制器或 Action 上标记允许的来源:

[<EnableCors(origins: "http://localhost:3000", headers: "*", methods: "*")>]
public class UserController : ApiController
{
    public IHttpActionResult Get() 
    {
        return Ok("Hello from API");
    }
}

全局配置更省事

如果多个控制器都需要跨域,一个个加特性太麻烦。可以直接在 Application_Start 中设置默认策略:

var cors = new EnableCorsAttribute("http://localhost:3000", "*", "*");
GlobalConfiguration.Configuration.EnableCors(cors);

这样所有 ApiController 都自动支持来自前端页面的请求。

Spring MVC 怎么办?

Java 阵营的 Spring MVC 同样支持跨域。可以在 Controller 上加 @CrossOrigin 注解:

@RestController
@CrossOrigin(origins = "http://localhost:3000")
public class UserController {
    
    @GetMapping("/user")
    public String getUser() {
        return "{\"name\": \"Tom\"};";
    }
}

也可以通过配置类统一处理:

@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST");

别忘了预检请求(Preflight)

有些请求会先发一个 OPTIONS 方法的预检请求,比如带自定义头或 Content-Type 为 application/json 的 POST。后端必须正确响应这个 OPTIONS 请求,否则实际请求根本不会发出。确保服务器对 OPTIONS 返回 200,或者用框架自带机制自动处理。

生产环境要小心

开发时设 origins 为 * 图个方便,但上线后最好明确指定前端域名。开放通配符等于把后端接口暴露给任意网站,万一被恶意页面调用,用户信息可能就被偷走了。

跨域不是 bug,是保护机制。理解它的工作原理,再在 MVC 框架里合理配置,就能让前后端顺畅协作,不被“拦路”卡住开发节奏。