从 Excel 中的 Power Query 访问 OData 源:禁止访问资源
Accessing OData Feed from Power Query in Excel: Access to resource is forbidden
我已经使用教程创建了一个本地托管的 Web API OData 服务。它使用同样在本地托管并通过代码优先方法创建的数据库。它在系统中有两个实体,供应商和产品。当我使用 localhostXXXX/Products 或 /Suppliersit 从浏览器访问 api 时,它会像这样显示提要:
{
"@odata.context":"http://localhost:64469/$metadata#Products","value":[
{
"Id":1,"Name":"Broken Age","Price":15.00,"Category":"Adventure","SupplierId":1
},{
"Id":2,"Name":"Final Fantasy IX","Price":10.00,"Category":"JRPG","SupplierId":2
},{
"Id":3,"Name":"Fallout 3","Price":15.00,"Category":"Action RPG","SupplierId":3
}
]
然而,当我尝试通过 PowerQuery 访问 Excel 中的提要时(通过转到“来自其他来源”->“来自 OData 提要”),它会显示一条提示消息:"Access to the resource is forbidden".
谁能明白这是为什么?还有我怎么可能绕过它?我没有对 Web 服务进行任何身份验证,因为它在 Web 浏览器中工作,所以很容易访问。
非常感谢任何人可以提供的帮助。
来源见下文。
控制器
public class SuppliersController : ODataController
{
ProductsContext db = new ProductsContext();
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
[EnableQuery]
public IQueryable<Supplier> Get()
{
return db.Suppliers;
}
[EnableQuery]
public SingleResult<Supplier> Get([FromODataUri] int key)
{
IQueryable<Supplier> result = db.Suppliers.Where(p => p.Id == key);
return SingleResult.Create(result);
}
[EnableQuery]
public IQueryable<Product> GetProducts([FromODataUri] int key)
{
return db.Suppliers.Where(m => m.Id.Equals(key)).SelectMany(m => m.Products);
}
}
public class ProductsController : ODataController
{
ProductsContext db = new ProductsContext();
private bool ProductExists(int key)
{
return db.Products.Any(p => p.Id == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
[EnableQuery]
public IQueryable<Product> Get()
{
return db.Products;
}
[EnableQuery]
public SingleResult<Product> Get([FromODataUri] int key)
{
IQueryable<Product> result = db.Products.Where(p => p.Id == key);
return SingleResult.Create(result);
}
[EnableQuery]
public SingleResult<Supplier> GetSupplier([FromODataUri] int key)
{
var result = db.Products.Where(m => m.Id == key).Select(m => m.Supplier);
return SingleResult.Create(result);
}
}
型号:
public class Supplier
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
[ForeignKey("Supplier")]
public int? SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
}
Web.Config
<connectionStrings>
<add name="ProductsContext" connectionString="Data Source=(localdb)\v11.0;
Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True;
AttachDbFilename=|DataDirectory|ProductsContext.mdf"
providerName="System.Data.SqlClient" />
</connectionStrings>
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// New code:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
builder.EntitySet<Supplier>("Suppliers");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
}
}
Configuration.cs
internal sealed class Configuration : DbMigrationsConfiguration<ProductService.Models.ProductsContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "ProductService.Models.ProductsContext";
}
protected override void Seed(ProductService.Models.ProductsContext context)
{
context.Suppliers.AddOrUpdate(x => x.Id,
new Supplier() {Id = 1, Name = "Double Fine" },
new Supplier() { Id = 2, Name = "Square Enix" },
new Supplier() { Id = 3, Name = "Bethesda" });
context.Products.AddOrUpdate(x => x.Id,
new Product() { Id = 1, SupplierId = 1, Name = "Broken Age", Price=15.00M, Category = "Adventure"},
new Product() { Id = 2, SupplierId = 2, Name = "Final Fantasy IX", Price = 10.00M, Category = "JRPG" },
new Product() { Id = 3, SupplierId = 3, Name = "Fallout 3", Price = 15.00M, Category = "Action RPG" });
}
}
ProductServiceContext.cs
public class ProductsContext : DbContext
{
public ProductsContext()
: base("name=ProductsContext")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Supplier> Suppliers { get; set; }
}
更新
正如 Curt Hagenlocher 所问,如果我在浏览器中转到 localhost64469/$metadata,下面是输出:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="ProductService.Models">
<EntityType Name="Product">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false"/>
<Property Name="Name" Type="Edm.String"/>
<Property Name="Price" Type="Edm.Decimal" Nullable="false"/>
<Property Name="Category" Type="Edm.String"/>
<Property Name="SupplierId" Type="Edm.Int32"/>
<NavigationProperty Name="Supplier" Type="ProductService.Models.Supplier"/>
</EntityType>
<EntityType Name="Supplier">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false"/>
<Property Name="Name" Type="Edm.String"/>
<NavigationProperty Name="Products" Type="Collection(ProductService.Models.Product)"/>
</EntityType>
</Schema>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Default">
<EntityContainer Name="Container">
<EntitySet Name="Products" EntityType="ProductService.Models.Product">
<NavigationPropertyBinding Path="Supplier" Target="Suppliers"/>
</EntitySet>
<EntitySet Name="Suppliers" EntityType="ProductService.Models.Supplier">
<NavigationPropertyBinding Path="Products" Target="Products"/>
</EntitySet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
您可能想查看 PowerQuery 发送到您的服务的请求,以及您的服务如何处理它。禁止错误只会在服务返回时在 PQ 中报告。
问题出在我 Excel 的 PowerQuery 插件版本上。它是一个过时的版本(我相信是 2.21 而不是 2.22),并且在最新版本中它添加了 OData v4 支持,这是我使用的 OData 版本。
感谢您的帮助。
我已经使用教程创建了一个本地托管的 Web API OData 服务。它使用同样在本地托管并通过代码优先方法创建的数据库。它在系统中有两个实体,供应商和产品。当我使用 localhostXXXX/Products 或 /Suppliersit 从浏览器访问 api 时,它会像这样显示提要:
{
"@odata.context":"http://localhost:64469/$metadata#Products","value":[
{
"Id":1,"Name":"Broken Age","Price":15.00,"Category":"Adventure","SupplierId":1
},{
"Id":2,"Name":"Final Fantasy IX","Price":10.00,"Category":"JRPG","SupplierId":2
},{
"Id":3,"Name":"Fallout 3","Price":15.00,"Category":"Action RPG","SupplierId":3
}
]
然而,当我尝试通过 PowerQuery 访问 Excel 中的提要时(通过转到“来自其他来源”->“来自 OData 提要”),它会显示一条提示消息:"Access to the resource is forbidden".
谁能明白这是为什么?还有我怎么可能绕过它?我没有对 Web 服务进行任何身份验证,因为它在 Web 浏览器中工作,所以很容易访问。
非常感谢任何人可以提供的帮助。
来源见下文。
控制器
public class SuppliersController : ODataController
{
ProductsContext db = new ProductsContext();
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
[EnableQuery]
public IQueryable<Supplier> Get()
{
return db.Suppliers;
}
[EnableQuery]
public SingleResult<Supplier> Get([FromODataUri] int key)
{
IQueryable<Supplier> result = db.Suppliers.Where(p => p.Id == key);
return SingleResult.Create(result);
}
[EnableQuery]
public IQueryable<Product> GetProducts([FromODataUri] int key)
{
return db.Suppliers.Where(m => m.Id.Equals(key)).SelectMany(m => m.Products);
}
}
public class ProductsController : ODataController
{
ProductsContext db = new ProductsContext();
private bool ProductExists(int key)
{
return db.Products.Any(p => p.Id == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
[EnableQuery]
public IQueryable<Product> Get()
{
return db.Products;
}
[EnableQuery]
public SingleResult<Product> Get([FromODataUri] int key)
{
IQueryable<Product> result = db.Products.Where(p => p.Id == key);
return SingleResult.Create(result);
}
[EnableQuery]
public SingleResult<Supplier> GetSupplier([FromODataUri] int key)
{
var result = db.Products.Where(m => m.Id == key).Select(m => m.Supplier);
return SingleResult.Create(result);
}
}
型号:
public class Supplier
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
[ForeignKey("Supplier")]
public int? SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
}
Web.Config
<connectionStrings>
<add name="ProductsContext" connectionString="Data Source=(localdb)\v11.0;
Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True;
AttachDbFilename=|DataDirectory|ProductsContext.mdf"
providerName="System.Data.SqlClient" />
</connectionStrings>
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// New code:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
builder.EntitySet<Supplier>("Suppliers");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
}
}
Configuration.cs
internal sealed class Configuration : DbMigrationsConfiguration<ProductService.Models.ProductsContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "ProductService.Models.ProductsContext";
}
protected override void Seed(ProductService.Models.ProductsContext context)
{
context.Suppliers.AddOrUpdate(x => x.Id,
new Supplier() {Id = 1, Name = "Double Fine" },
new Supplier() { Id = 2, Name = "Square Enix" },
new Supplier() { Id = 3, Name = "Bethesda" });
context.Products.AddOrUpdate(x => x.Id,
new Product() { Id = 1, SupplierId = 1, Name = "Broken Age", Price=15.00M, Category = "Adventure"},
new Product() { Id = 2, SupplierId = 2, Name = "Final Fantasy IX", Price = 10.00M, Category = "JRPG" },
new Product() { Id = 3, SupplierId = 3, Name = "Fallout 3", Price = 15.00M, Category = "Action RPG" });
}
}
ProductServiceContext.cs
public class ProductsContext : DbContext
{
public ProductsContext()
: base("name=ProductsContext")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Supplier> Suppliers { get; set; }
}
更新
正如 Curt Hagenlocher 所问,如果我在浏览器中转到 localhost64469/$metadata,下面是输出:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="ProductService.Models">
<EntityType Name="Product">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false"/>
<Property Name="Name" Type="Edm.String"/>
<Property Name="Price" Type="Edm.Decimal" Nullable="false"/>
<Property Name="Category" Type="Edm.String"/>
<Property Name="SupplierId" Type="Edm.Int32"/>
<NavigationProperty Name="Supplier" Type="ProductService.Models.Supplier"/>
</EntityType>
<EntityType Name="Supplier">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false"/>
<Property Name="Name" Type="Edm.String"/>
<NavigationProperty Name="Products" Type="Collection(ProductService.Models.Product)"/>
</EntityType>
</Schema>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Default">
<EntityContainer Name="Container">
<EntitySet Name="Products" EntityType="ProductService.Models.Product">
<NavigationPropertyBinding Path="Supplier" Target="Suppliers"/>
</EntitySet>
<EntitySet Name="Suppliers" EntityType="ProductService.Models.Supplier">
<NavigationPropertyBinding Path="Products" Target="Products"/>
</EntitySet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
您可能想查看 PowerQuery 发送到您的服务的请求,以及您的服务如何处理它。禁止错误只会在服务返回时在 PQ 中报告。
问题出在我 Excel 的 PowerQuery 插件版本上。它是一个过时的版本(我相信是 2.21 而不是 2.22),并且在最新版本中它添加了 OData v4 支持,这是我使用的 OData 版本。
感谢您的帮助。