Breeze executeQuery Q 承诺失败 CORS

Posted

技术标签:

【中文标题】Breeze executeQuery Q 承诺失败 CORS【英文标题】:Breeze executeQuery Q promise fails CORS 【发布时间】:2013-06-28 04:14:06 【问题描述】:

我在使用 Breeze 使用 OData 服务时遇到问题,我按照 this guide 设置了一个 Web API OData 服务,来自 Fiddler,它按预期工作得很好,但是当我尝试使用它时,它会失败并且给出“OK”的错误信息:

[Q] Unhandled rejection reasons (should be empty):Error: OK

使用 fiddler 我看到它会查询元数据,然后查询正确返回的实体,这可能是什么问题?

breeze.config.initializeAdapterInstances( dataService: "OData" );
var manager = new breeze.EntityManager(serverAddress);

var query = new breeze.EntityQuery.from("Laboratories");

manager.executeQuery(query).then(function (data) 
    ko.applyBindings(data);
).fail(function (e) 
    alert(e);
);

我通过使用ASP.NET Web API CORS support 的每晚构建启用了 CORS,一切正常,我可以检索实体,因为我可以在提琴手中看到它们被返回......只是它没有到​​那时承诺反而失败了。

更新:

为了响应来自新创建项目的@Ward 测试,我执行了以下操作:

项目 1

创建了一个 Web API 项目。

添加了来自 Nuget 的 Microsoft ASP.MET Web API 跨域资源共享 (CORS) 参考。

添加了以下控制器:

namespace CORSBreezeTest1.Controllers

    public class ValuesController : EntitySetController<Value, int>
    
        ValuesDbContext _context = new ValuesDbContext();

        [Queryable]
        public override IQueryable<Value> Get()
        
            return _context.Values;
        

        protected override Value GetEntityByKey(int key)
        
            return _context.Values.Find(key);
        

        protected override Value CreateEntity(Value entity)
        
            Value value = _context.Values.Find(entity.Id);
            if (value != null)
            
                throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Conflict));
            
            _context.Values.Add(entity);
            _context.SaveChanges();
            return entity;
        

        protected override int GetKey(Value entity)
        
            return entity.Id;
        

        protected override void Dispose(bool disposing)
        
            _context.Dispose();
            base.Dispose(disposing);
        
    

还有以下 Code First 数据库:

namespace CORSBreezeTest1

    public class ValuesDbContext : DbContext
    
        public ValuesDbContext()
            : base("DefaultConnection")
        

        

        public DbSet<Value> Values  get; set; 
    

    public class Value
    
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id  get; set; 

        public string Name  get; set; 

        public int Quantity  get; set; 
    

WebApiConfig.cs中添加了以下几行

public static void Register(HttpConfiguration config)


 // Default code left out here ...

 config.Routes.MapODataRoute("Values", "odata", GetEdmModel());
 config.EnableQuerySupport();
 config.EnableCors(new EnableCorsAttribute("*", "*", "*"));



private static IEdmModel GetEdmModel()

    ODataModelBuilder builder = new ODataConventionModelBuilder();
    builder.Namespace = "CORSBreezeTest1";
    builder.EntitySet<Value>("Values");
    return builder.GetEdmModel();

项目 2 然后创建了另一个 Web API 项目。

为 ASP.NET Web API 项目 Nuget 包添加了微风

添加了 datajs Nuget 包。

将以下代码行添加到Index.cshtml

<p data-bind="visible: !results">Fetching data ... </p>
<ul data-bind="foreach: results, visible: results" style="display: none">
    <li>
        <span data-bind="text: Name"></span>
        <span data-bind="text: Quantity"></span>
    </li>
</ul>


@section Scripts 
    <script src="~/Scripts/knockout-2.2.0.debug.js"></script>
    <script src="~/Scripts/q.js"></script>
    <script src="~/Scripts/datajs-1.1.0.js"></script>
    <script src="~/Scripts/breeze.min.js"></script>
    <script type="text/javascript">
        $(function () 
            breeze.config.initializeAdapterInstances( dataService: "OData" );
            var manager = new breeze.EntityManager("http://serverAddress/odata")
            var query = new breeze.EntityQuery.from("Values");
            manager.executeQuery(query).then(function (data) 
                ko.applyBindings(data);
            ).fail(function (e) 
                alert(e);
            );
        );
    </script>

按原样测试,因为两个网站都在本地主机上,所以它可以正常工作。

将 PROJECT 1 发布到 Web 服务器,以便测试实际上会看到不同的来源并进行测试。

这就是 Nugget 看到的:

第一个请求头是 OPTIONS

OPTIONS /odata/Values HTTP/1.1

第二个请求头是GET

GET /odata/Values HTTP/1.1

如果我将 fail 代码更改为:

fail(function (e) 
 ko.applyBindings(e.body.value);
);

还有我的淘汰代码:

<p data-bind="visible: !$data">Fetching data ... </p>
<ul data-bind="foreach: $data, visible: $data" style="display: none">
    <li>
        <span data-bind="text: Name"></span>
        <span data-bind="text: Quantity"></span>
    </li>
</ul>

瞧!它是通过数据得出的:

这就是控制台看到的:

SEC7118: XMLHttpRequest for http://serverAddress/odata/$metadata required Cross Origin Resource Sharing (CORS). 
localhost:53317
SEC7119: XMLHttpRequest for http://serverAddress/odata/$metadata required CORS preflight. 
localhost:53317
SEC7118: XMLHttpRequest for http://serverAddress/odata/Values required Cross Origin Resource Sharing (CORS). 
localhost:53317
SEC7119: XMLHttpRequest for http://serverAddress/odata/Values required CORS preflight. 
localhost:53317
[Q] Unhandled rejection reasons (should be empty):Error: OK 

项目 1 和 2 使用 BreezeControllerAttribute

如果我在另一个测试中在Breeze Nuget example 之后添加一个新控制器并添加 Breeze for ASP.NET Web API 项目 Nuget 包并添加以下控制器:

namespace CORSBreezeTest1.Controllers

    [BreezeController]
    public class BreezeValuesController : ApiController
    
        readonly EFContextProvider<ValuesDbContext> _context =
            new EFContextProvider<ValuesDbContext>();

        [HttpGet]
        public string Metadata()
        
            return _context.Metadata();
        

        [HttpGet]
        public IQueryable<Value> Values()
        
            return _context.Context.Values;
        

        [HttpPost]
        public SaveResult SaveChanges(JObject saveBundle)
        
            return _context.SaveChanges(saveBundle);
        

        protected override void Dispose(bool disposing)
        
            base.Dispose(disposing);
        
    

然后修改客户端如下:

//breeze.config.initializeAdapterInstances( dataService: "OData" );
var manager = new breeze.EntityManager("http://serverAddress/breeze/BreezeValues")

然后请求发生变化:

一切正常...我不确定是EntitySetController 处理请求的方式不同,还是Breeze 在更改dataService 时发出了不同的请求。

【问题讨论】:

每当我在 Chrome 中收到 [should be empty] 错误时,都是由于没有解决承诺。只是为了我的理解,您并没有说明要在本地或从本地缓存查询的任何地方,对吗? 不,我必须根据我发现的一些东西重写问题...我正在查询另一个网站,所以我在服务器上启用了 CORS ...使用提琴手查询可以正常工作,只是从另一个站点查询是失败的地方,尽管在提琴手中我可以看到,当它查询时它确实返回了结果......如果我使用指南中的 ASP.NET Web API OData nuget 包,它不适用于微风给结果,但如果我采用 Breeze Web API nuget 包的方式,它可以工作...... 如果我在托管 Web API 的同一个站点中使用相同的代码,它也可以完美运行...... 【参考方案1】:

我还不知道答案。但是你的问题有几个误解。首先,CORS 是一种浏览器约定。这是一种(相对)安全的解决浏览器“同源”策略的方法。

这是浏览器政策。 Fiddler 不是浏览器。它不受“同源”策略的限制,可以愉快地跨域读写。因此,根据 Fiddler 中发生的情况,您无法判断服务器是否为 CORS 正确配置。

当然,“托管 Web API 的同一站点中的相同代码可以完美运行”;您没有违反“同源”政策,因此不涉及 CORS。

您需要通过编写一个从 Web API 主机以外的站点启动的浏览器客户端应用程序来测试您的服务器配置。它不一定是轻而易举的客户。对端点的简单 AJAX 调用就可以了。您不妨在使用时编写另一个简单的非 Breeze Web API 控制器。保持两个控制器非常简单。让您的测试客户端非常简单。

我敢打赌,在启用微风的 Web API 控制器和普通 Web API 控制器上,您都会遇到同样的问题。你会通过它的力量。当你这样做时,它应该适用于微风控制器和香草控制器。如果您能证明您的客户使用其中一个而不是另一个,请返回并给我们代码。

很抱歉给您带来痛苦。

【讨论】:

它实际上适用于简单的 ajax 调用,并使用 BreezeControllerAttribute 装饰控制器,它工作正常......我提到我在提琴手中看到了一些东西,因为当我启动我的测试网络应用程序提琴手捕获了通信和显示了结果......实际上,如果我通过失败承诺处理事情,我实际上可以通过 e.body.value 检索数据......但我将创建两个新的 web api 来测试也许我做了一些让它工作的事情错了……我会回来的 也许我们在解释服务器响应时遇到了问题。我渴望了解您的发现并有一个示例可供探索。如果我有更多的时间,我会自己旋转一些东西。 Web API CORS 支持还不是官方的(我不认为)所以我必须优先考虑。 添加了测试结果,如果我遗漏了什么请告诉我,谢谢 只是为了澄清 CORS,我使用 ToDos 示例中的 BreezeSimpleCorsHandler 进行了测试......无论是微风方式还是 web api 方式都可以很好地启用 CORS......Breeze 可能会有所不同行为? 非常有帮助,谢谢。现在我们有一些东西要深入研究。我的直觉是,当 Breeze 试图将原始 JSON 数据转换为实体时,它会在内部抛出异常。在我真正深入研究之前,还有两个问题:你的 Breeze 版本是什么(查看微风.debug.js 的顶部)?浏览器控制台窗口是否报告任何错误或者您所显示的错误程度(我担心后者)?【参考方案2】:

让它工作的唯一方法是使用来自Breeze.WebApi 的BreezeControllerAttribute,遵循使用 api 的微风精确方式。不使用EntitySetController 并回到常规ApiController。问题本身的详细解释。

[BreezeController]
public class BreezeValuesController : ApiController

    // Methods here

【讨论】:

【参考方案3】:

你只需要添加这个额外的参数DataServiceVersionMaxDataServiceVersion配置enableCors。

config.EnableCors(new EnableCorsAttribute("*", "*", "*", "DataServiceVersion, MaxDataServiceVersion"));

【讨论】:

以上是关于Breeze executeQuery Q 承诺失败 CORS的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose.create + Q 将 mongoose 承诺转换为 Q 承诺

猫鼬承诺和Q承诺

像在 Q 中定义空的 Bluebird 承诺

节点 q 承诺递归

猫鼬和 q 承诺

nodejs 中的异步和 Q 承诺