I'm having a problem with consuming OData Services using Breeze, I set up a Web API OData service by following this guide, from Fiddler it works excellent as expected, but when I try to use it with breeze it fails and gives an error message of "OK":
[Q] Unhandled rejection reasons (should be empty):Error: OK
Using fiddler I see it goes and queries for metadata and then it queries for the entities which are returned correctly, what could be the problem here?
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);
});
I enabled CORS by using the nightly build of ASP.NET Web API CORS support, it all works fine and I can retrieve the entities since I can see in fiddler that they are returned ... it's just that it doesn't go to the then promise instead it lands in fail.
UPDATE:
In response to @Ward testing from newly created projects I did the following:
PROJECT 1
Created a Web API Project.
Added Microsoft ASP.MET Web API Cross-Origin Resource Sharing (CORS) Reference from Nuget.
Added the following Controller:
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);
}
}
}
And the following Code First Database:
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; }
}
}
Added the following lines in 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();
}
PROJECT 2 Then created another Web API Project.
Added Breeze for ASP.NET Web API Projects Nuget Package
Added datajs Nuget Package.
Added the following lines of code to 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>
}
Tested as is and it worked since both websites are on localhost.
Published PROJECT 1 to a web server so that the test will actually see different origins, and tested.
And this is what Nugget saw:
The first request headers are OPTIONS
OPTIONS /odata/Values HTTP/1.1
And the second request headers are GET
GET /odata/Values HTTP/1.1
And if I change my fail
code to:
fail(function (e) {
ko.applyBindings(e.body.value);
});
And my knockout code to:
<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>
Voila! It came through with the data:
And this is what the Console saw:
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
PROJECTS 1 & 2 using the BreezeControllerAttribute
If I in another test add a new controller following Breeze Nuget example and add Breeze for ASP.NET Web API project Nuget package and add the following controller:
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);
}
}
}
And then modify the client as following:
//breeze.config.initializeAdapterInstances({ dataService: "OData" });
var manager = new breeze.EntityManager("http://serverAddress/breeze/BreezeValues")
Then the requests change:
And everything works ... I'm not sure if in part is something that EntitySetController
handles requests differently or is Breeze making different requests when changing the dataService
.