簡體   English   中英

為什么來自 Angular 9 服務的 HTTP PUT 方法創建一個新的數據庫實體而不是更新原始數據庫實體?

[英]Why does HTTP PUT method from Angular 9 service CREATE a new DB entity instead of UPDATING the original?

我一般是 Angular SPA 和 MVC 的新手,我目前在 Visual Studio 2017 中使用 EF Core ORM(模型)和 Angular 9 前端在 MVC Core 2.2 Web 應用程序項目上工作。 該項目的目的是學習 Angular/AspNetCore MVC/EF Core 以替換使用 WCF 和 EF 6 的 .NET 4.5 Winforms 應用程序。我正在使用的源代碼示例來自同一作者的三本不同的書籍,我正在努力學習如何將所有部分組合在一起。

我的測試應用程序有一個 MVC API 控制器,它接收來自我的 Angular 服務的 HTTP 請求。 使用我的 Angular 模板,我可以從我的本地 MSSQL 數據庫中獲取一個或所有項目,並且我可以毫無問題地創建和刪除項目。 但是,當我嘗試更新現有項目時,結果是使用更新的數據創建了另一個項目,而原始項目仍然與舊數據一起存在。

當我編輯 ID 2010 並更改名稱和價格時,我得到一個新創建的國際象棋項目 - 而不是更新的 #2010。

角度模板 1

這是我的 Angular 服務:

(other imports above)
import { Product } from "./product.model";

export const REST_URL = new InjectionToken("rest_url");

@Injectable()
export class RestDataSource {

  constructor(private http: HttpClient, @Inject(REST_URL) private url: string) { }

  getData(): Observable<Product[]> {
        return this.sendRequest<Product[]>("GET", this.url);
  }

  saveProduct(product: Product): Observable<Product> {
        return this.sendRequest<Product>("POST", this.url, product);
  }

  updateProduct(product: Product): Observable<Product> {
    return this.sendRequest<Product>("PUT", this.url, product);
  }

  deleteProduct(id: number): Observable<Product> {
    return this.sendRequest<Product>("DELETE", `${this.url}/${id}`);
  }

  private sendRequest<T>(verb: string, url: string, body?: Product)
    : Observable<T> {
    return this.http.request<T>(verb, url, {
          body: body,
          headers: new HttpHeaders({
            "Access-Key": "<secret>",
            "Application-Name": "exampleApp"
            })
        });
    }
  }

這是我的 Angular 模型功能模塊:

 import { NgModule } from "@angular/core";
 import { Model } from "./repository.model";
 import { HttpClientModule, HttpClientJsonpModule } from "@angular/common/http";
 import { RestDataSource, REST_URL } from "./rest.datasource";

 @NgModule({
   imports: [HttpClientModule, HttpClientJsonpModule],
   providers: [Model, RestDataSource,
       { provide: REST_URL, useValue: `http://${location.hostname}:51194/api/products` }]
})
export class ModelModule { }

這是我的 Angular 9 model.repository.ts:

  import { Injectable } from "@angular/core";
  import { Product } from "./product.model";
  import { Observable } from "rxjs";
  import { RestDataSource } from "./rest.datasource";
  
  @Injectable()
  export class Model {
    private products: Product[] = new Array<Product>();
    private locator = (p: Product, id: number) => p.id == id;
  
      constructor(private dataSource: RestDataSource) {
        this.dataSource.getData().subscribe(data => this.products = data);
      }

    //removed GET, DELETE methods that are working

        //this method below is supposed to CREATE (POST) if Product has no ID 
        //and UPDATE (PUT) if there is a Product ID, but the update is not working

      saveProduct(product: Product) {
        if (product.id == 0 || product.id == null) {
           this.dataSource.saveProduct(product).subscribe(p => this.products.push(p));
        } else {
          this.dataSource.updateProduct(product).subscribe(p => {
            const index = this.products.findIndex(item => this.locator(item, p.id));
            this.products.splice(index, 1, p);
          });
      }
    }
  }

使用 Chrome F12 的 HTTP PUT 請求方法圖片: 鉻 F12

這是我的 MVC API 控制器:

    using Microsoft.AspNetCore.Mvc;
    using Core22MvcNg9.Models;

    namespace Core22MvcNg9.Controllers {

    [Route("api/products")]
    public class ProductValuesController : Controller {
      private IWebServiceRepository repository;

      public ProductValuesController(IWebServiceRepository repo) 
          => repository = repo;
  
      [HttpGet("{id}")]
      public object GetProduct(int id) {
          return repository.GetProduct(id) ?? NotFound();
      }
  
      [HttpGet]
      public object Products() { 
          return repository.GetProducts(); 
      }
  
      [HttpPost]
      public int StoreProduct([FromBody] Product product) {
          return repository.StoreProduct(product);
      }
  
      [HttpPut]
      public void UpdateProduct([FromBody] Product product) {
          repository.UpdateProduct(product);
      }
  
      [HttpDelete("{id}")]
      public void DeleteProduct(int id) {
          repository.DeleteProduct(id);
      }
   }
  }

這是 MVC 模型 Web 服務存儲庫(接口):

  namespace Core22MvcNg9.Models {

     public interface IWebServiceRepository {
  
       object GetProduct(int id);

       object GetProducts();

       int StoreProduct(Product product);

       void UpdateProduct(Product product);

       void DeleteProduct(int id);
      }
  }

這是 MVC Web 服務模型存儲庫實現類:

  using System.Linq;
  using Microsoft.EntityFrameworkCore;
  
  namespace Core22MvcNg9.Models {
  
  public class WebServiceRepository : IWebServiceRepository {
       private ApplicationDbContext context;
  
       public WebServiceRepository(ApplicationDbContext ctx) => context = ctx;
  
       public object GetProduct(int id)
       {
           return context.Products.Include(p => p.Category)
               .Select(p => new {
                   Id = p.ProductID,
                   Name = p.Name,
                   Category = p.Category,
                   Price = p.Price
               })
               .FirstOrDefault(p => p.Id == id);
       }
       
       public object GetProducts() {
           return context.Products.Include(p => p.Category)
               .OrderBy(p => p.ProductID)
               .Select(p => new {
                   Id = p.ProductID,
                   Name = p.Name,
                   Category = p.Category,
                   Price = p.Price
                });
       }
  
       public int StoreProduct(Product product) {
           context.Products.Add(product);
           context.SaveChanges();
           return product.ProductID;
       }
  
       public void UpdateProduct(Product product) {
           context.Products.Update(product);
           context.SaveChanges();
       }
  
       public void DeleteProduct(int id) {
           context.Products.Remove(new Product { ProductID = id });
           context.SaveChanges();
       }
    }
  }

這是 MVC Startup.cs:

  (other using statements above)    
  using Microsoft.AspNetCore.SpaServices.AngularCli;
  using Microsoft.Extensions.DependencyInjection;
  using Core22MvcNg9.Models;

  namespace Core22MvcNg9
  {
      public class Startup
      {
          public Startup(IConfiguration configuration)
          {
              Configuration = configuration;
          }

          public IConfiguration Configuration { get; }

          // Use this method to add services to the container.
          public void ConfigureServices(IServiceCollection services)
          {
              services.AddDbContext<ApplicationDbContext>(options => 
                  options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
              services.AddTransient<IProductRepository, EFProductRepository>();
              services.AddTransient<IWebServiceRepository, WebServiceRepository>();
              services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        
              // In production, the Angular files will be served from this directory
              services.AddSpaStaticFiles(configuration =>
              {
                  configuration.RootPath = "exampleApp/dist"; 
              });
          }

          // Use this method to configure the HTTP request pipeline.
          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
          {
              if (env.IsDevelopment())
              {
                  app.UseDeveloperExceptionPage();
                  app.UseStatusCodePages();
              }
              else
              {
                  app.UseExceptionHandler("/Error");
              }

              app.UseStaticFiles();
              app.UseSpaStaticFiles();
        
              SeedData.EnsurePopulated(app);

              app.UseMvc(routes =>
              {
                  routes.MapRoute(
                      name: "pagination",
                      template: "Products/Page{productPage}",
                      defaults: new { Controller = "Product", action = "List" });
              });

              app.UseSpa(spa =>
              {

                  spa.Options.SourcePath = "exampleApp"; 

                  if (env.IsDevelopment())
                  {
                      spa.UseAngularCliServer(npmScript: "start");
                  }
              });
          }
      }
  }

你能幫我嗎? 感謝您耐心閱讀長篇文章,如果您需要其他任何東西,請告訴我。

關於@derpirscher 評論的指導:

我在 API 控制器方法 UpdateProduct([FromBody] Product product) 的 MVC 代碼中設置了一個斷點。

此方法將 product.ProductID 值顯示為 0,因此該方法沒有像 [FromBody] 屬性所暗示的那樣在消息正文中找到“ProductID”。

這提醒我,Angular 數據模型使用“id”作為產品的標識,而不是 ProductID——我在 MVC 代碼和模型中更改了它,包括數據上下文。

因此,我將 MVC 模型/存儲庫和控制器中的數據上下文更改回產品標識的“Id”,使用 dotnet 遷移刪除並重建數據庫,更新現在正在運行,將 Angular 服務中的“id”匹配到使用 [From Body] 的 MVC API 控制器中的“Id”。

我的 Angular 9 html/組件仍然需要努力解決“屬性 'id' 為 null”的問題,但我很高興 MVC 更新現在正在工作。

PUT請求不會隨機更改為POST請求。 還有你的瀏覽器開發工具截圖顯示,該請求確實是一個PUT請求。

錯誤可能出在服務器上。 設置斷點並檢查當您點擊PUT端點時會發生什么以及在context.Products.Updatecontext.SaveChanges會發生什么。 也許請求正文在服務器上沒有被正確解釋,所以不是更新而是插入......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM