zoukankan      html  css  js  c++  java
  • 在ASP.NET Web API中使用OData的Action和Function

    本篇体验OData的Action和Function功能。上下文信息参考"ASP.NET Web API基于OData的增删改查,以及处理实体间关系"。在本文之前,我存在的疑惑包括:

    ● 为什么需要OData的Action和Function功能?
    ● Action和Function之间有什么区别?
    ● 如何设置OData的的Action和Function功能?这中间有哪些惯例呢?

    为某个Product添加Action

    如果我们希望通过http://localhost:54714/odata/Products(1)/SomeActionName来实现在某个Product基础上,为Product的关联表ProductRating添加一条数据,该如何做到呢?

    首先创建产品评论的一个模型:

    public class ProductRating
    {
        public int ID { get; set; }
        public int Rating { get; set; }
        public int ProductID { get; set; }
        public virtual Product Product { get; set; }
    }

    把模型添加到上下文中:

    public class ProductsContext : DbContext
    {
        public ProductsContext()
               : base("name=ProductsContext")
        {
        }
        public DbSet<Product> Products { get; set; }
        public DbSet<Supplier> Suppliers { get; set; }
        public DbSet<ProductRating> Ratings { get; set; }
    }

    添加迁移并更新数据库:

    Add-Migration "AddProductRating" -StartUpProjectName ProductService -ProjectName ProductService
    Update-Database  -StartUpProjectName ProductService -ProjectName ProductService  

    现在,需要在WebApiConfig.cs中的Register方法中,为Product的EDM添加一个Action。

    ODataModelBuilder builder = new ODataConventionModelBuilder();
    builder.Namespace = "ProductService";
    
    builder.EntitySet<Product>("Products");//创建EntityDataModel(EDM)
    builder.EntitySet<Supplier>("Suppliers");
    
    
    //http://localhost/Products(1)/ProductService.Rate 注意需要在Web.config中添加配置,因为在IIS上不允许带点,否则会返回404
    builder.EntityType<Product>()
        .Action("Rate")//给EDM添加一个Action
        .Parameter<int>("Rating"); //Rating作为参数在前后端传递
    
    
    
    config.MapODataServiceRoute(
        routeName: "ODataRoute",
        routePrefix: "odata", 
        model:builder.GetEdmModel());

    以上,

    ● 通过builder.Namespace定义了Action的命名空间为ProductService
    ● 通过Action方法给Product这个EDM定义了Rate这个Action
    ● 通过Parameter<T>方法定义了参数



    这意味着:

    ● 我们发出的请求格式大致是:http://localhost:54714/odata/Products(1)/ProductService.Rate
    ● API的action方法中,action名称是Rate,可以从前端接受一个名称为Rating的键值



    可问题还有:

    ● 前端如何把Rating这个参数传递出去呢?
    ● 后端又如何接受这个Rating参数呢?

    来看后端控制器部分的action,在ProductsController中添加如下:

    //这里的action名称Rate必须和EDM定义的时候保持一致
    [HttpPost]
    public async Task<IHttpActionResult> Rate([FromODataUri] int key, ODataActionParameters parameters)
    {
        //先验证
        if(!ModelState.IsValid)
        {
            return BadRequest();
        }
    
        //再取值
        int rating = (int)parameters["Rating"];
    
        //实施操作
        db.Ratings.Add(new ProductRating
        {
            ProductID = key,
            Rating = rating
        });
    
        //捕获异常
        try
        {
            await db.SaveChangesAsync();
        }
        catch (DbUpdateException ex)
        {
            if (!ProductExists(key))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
    
        return StatusCode(HttpStatusCode.NoContent);
    
    }

    可见,后端是通过ODataActionParameters来接受前端传来的变量Rating。

    现在前端可以试着发出请求了:

    POST http://localhost:54714/odata/Products(1)/ProductService.Rate
    Body {"Rating":5}

    我们把Rating变量放在了Body中,以json传递给后端。

    可是,返回结果是: 404 Not Found

    这是因为,IIS还不接受类似ProductService.Rate这样的写法,在Web.config添加如下配置:

    <system.webServer>
    <handlers>
      ...
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="/odata/*" verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    </system.webServer>

    重新请求,返回:204 No Content

    为Product集合添加Action

    如果想为Products集合添加如下方法发出如下请求:http://localhost:54714/odata/Products/ProductService.MostExpensive

    首先还是配置EDM:

    builder.EntityType<Product>().Collection
            .Function("MostExpensive")
            .Returns<double>();
            

    在ProductsController中添加如下Action:

    [HttpGet]
    public IHttpActionResult MostExpensive()
    {
        var product = db.Products.Max(x => x.Price);
        return Ok(product);
    }

    前端请求:

    GET http://localhost:54714/odata/Products/ProductService.MostExpensive
    返回:
    {
      "@odata.context": "http://localhost:54714/odata/$metadata#Edm.Decimal",
      "value": 18.2
    }

    和EDM模型无关,添加Function

    当我们需要添加一个与EDM 模型无关的方法时候,就使用Function。

    首先在WebApi.config中配置如下:

     builder.Function("GetSalesTaxRate")
                .Returns<double>()
                .Parameter<int>("PostalCode");

    在某个Controller中添加如下:

    [HttpGet]
    [ODataRoute("GetSalesTaxRate(PostalCode={postalCode})")]
    public IHttpActionResult GetSalesTaxRate([FromODataUri] int postalCode)
    {
        double rate = 5.8;
        return Ok(rate);
    }

    以上,ODataRoute设定路由规则。前端发出如下请求:

    GET http://localhost:54714/odata/GetSalesTaxRate(PostalCode=10)
    返回:

    {
      "@odata.context": "http://localhost:54714/odata/$metadata#Edm.Double",
      "value": 5.8
    }


    总结:当需要针对某个EDM模型或EDM模型集合进行CRUD以外的操作,甚至涉及多个模型的操作使用Action,但添加和EDM模型无关的操作,使用Function。

  • 相关阅读:
    python自动生成bean类
    CVPR2021 | SETR: 使用 Transformer 从序列到序列的角度重新思考语义分割
    经典论文系列 | 缩小Anchor-based和Anchor-free检测之间差距的方法:自适应训练样本选择
    单阶段实例分割综述
    CVPR2021提出的一些新数据集汇总
    使用 PyTorch Lightning 将深度学习管道速度提高 10 倍
    C#中使用ref和out传参的方法及区别
    读书笔记《重构 改善既有代码的设计》(第2版本)
    《大话设计模式》等读后感
    OOP、封装、继承、多态,真的懂了吗?
  • 原文地址:https://www.cnblogs.com/darrenji/p/4947078.html
Copyright © 2011-2022 走看看