【问题标题】:Calling Web API not working the first time in Blazor server side app第一次在 Blazor 服务器端应用程序中调用 Web API 不起作用
【发布时间】:2021-04-03 07:29:03
【问题描述】:

当我启动我的 ASP.Net Core Blazor 服务器端应用程序时,我第一次单击“保存”按钮时,没有任何反应。发送到控制器/API 的帖子会触发,但我的控制器/API 中的 PostListing 函数永远不会被调用。但是,如果我在之后第二次单击“保存”按钮,它会按预期工作。这是怎么回事?感谢任何可以提供帮助的人。

这是我的页面:

@page "/fetchdata"
@using SellEverywhere.Data
@using SellEverywhere.Models
@using System.Net.Http.Json
@inject HttpClient Http


<h1>Your Listings</h1>

<table width="100%" style="background:#05163D;color:honeydew">
    <tr>
        <td width="20"> </td>
        <td>
            <h2> Add New Listing Details</h2>
        </td>
        <td> </td>
        <td align="right">
            <button class="btn btn-info" @onclick="AddNewListing">Add New Listing</button>
        </td>
        <td width="10"> </td>
    </tr>
    <tr>
        <td colspan="2"></td>
    </tr>
</table>
<hr />
<form>
    <table class="form-group">
        <tr>
            <td>
                <label for="Name" class="control-label">ID</label>
            </td>
            <td>
                <input type="text" class="form-control" @bind="@lsts.Id" readonly />
            </td>
            <td width="20"> </td>
        </tr>
        <tr>
            <td>
                <label for="Name" class="control-label">Title</label>
            </td>
            <td>
                <input type="text" class="form-control" @bind="@lsts.Title" />
            </td>
            <td width="20"> </td>
            <td>
                <label for="Description" class="control-label">Description</label>
            </td>
            <td>
                <input type="text" class="form-control" @bind="@lsts.Description" />
            </td>
            <td width="20"> </td>
            <td>
                <label for="Name" class="control-label">Brand</label>
            </td>
            <td>
                <input type="text" class="form-control" @bind="@lsts.Brand" />
            </td>
        </tr>
        <tr>
            <td>
                <label for="Name" class="control-label">Size</label>
            </td>
            <td>
                <input type="text" class="form-control" @bind="@lsts.Size" />
            </td>
            <td width="20"> </td>
            <td>
                <label for="Name" class="control-label">Color</label>
            </td>
            <td>
                <input type="text" class="form-control" @bind="@lsts.Color" />
            </td>
            <td width="20"> </td>
            <td>
                <label for="Name" class="control-label">Condition</label>
            </td>
            <td>
                <input type="text" class="form-control" @bind="@lsts.Condition" />
            </td>
            <td width="20"> </td>
            <td></td>
            <td>
                <button type="submit" class="btn btn-success" @onclick="(async () => await AddListing())" style="width:220px;">Save</button>
            </td>
        </tr>
    </table>
</form>

<table width="100%" style="background:#0A2464;color:honeydew">
    <tr>
        <td width="20"> </td>
        <td>
            <h2>Listing Details</h2>
        </td>

    </tr>
    <tr>
        <td colspan="2"></td>
    </tr>
</table>

@if (listing == null)
{
    <p><em>No listing found...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Id</th>
                <th>Title</th>
                <th>Brand</th>
                <th>Color</th>
                <th>Condition</th>
                <th>Size</th>
                <th>Description</th>
                <th>Tag1</th>
                <th>Tag2</th>
                <th>Tag3</th>
                <th>Price</th>
                <th>Lowest Price</th>
                <th>Shipping Price</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var lst in listing)
            {
            <tr>
                <td>@lst.Id</td>
                <td>@lst.Title</td>
                <td>@lst.Brand</td>
                <td>@lst.Color</td>
                <td>@lst.Condition</td>
                <td>@lst.Size</td>
                <td>@lst.Description</td>
                <td>@lst.Tag1</td>
                <td>@lst.Tag2</td>
                <td>@lst.Tag3</td>
                <td>@lst.Price</td>
                <td>@lst.LowestPrice</td>
                <td>@lst.ShippingPrice</td>
                <td><button class="btn btn-primary" @onclick="@(async () => await EditListing(@lst.Id))" style="width:110px;">Edit</button></td>
                <td><button class="btn btn-danger" @onclick="@(async () => await DeleteListing(@lst.Id))">Delete</button></td>
            </tr>

            }
        </tbody>
    </table>
}

@code {
    Listing[] listing;
    Listing lsts = new Listing();
    string ids = "0";
    //bool showAddrow = false;
    protected override async Task OnInitializedAsync()
    {
        listing = await Http.GetFromJsonAsync<Listing[]>("https://localhost:44324/api/Listings/");
    }

    void AddNewListing()
    {
        lsts = new Listing();
    }
    // Add New Listings Details Method
    protected async Task AddListing()
    {
        if (lsts.Id == 0)

        {
            await Http.PostAsJsonAsync("https://localhost:44324/api/Listings/", lsts);
        }
        else
        {
            await Http.PutAsJsonAsync("https://localhost:44324/api/Listings/" + lsts.Id, lsts);
        }
        lsts = new Listing();
        listing = await Http.GetFromJsonAsync<Listing[]>("https://localhost:44324/api/Listings/");
    }
    // Edit Method
    protected async Task EditListing(int listingID)
    {
        ids = listingID.ToString();
        lsts = await Http.GetFromJsonAsync<Listing>("https://localhost:44324/api/Listings/" + Convert.ToInt32(listingID));
    }
    // Delete Method
    protected async Task DeleteListing(int listingID)
    {
        ids = listingID.ToString();
        await Http.DeleteAsync("https://localhost:44324/api/Listings/" + Convert.ToInt32(listingID));
        listing = await Http.GetFromJsonAsync<Listing[]>("https://localhost:44324/api/Listings/");
    }

}

这是我的控制器/API:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SellEverywhere.Data;
using SellEverywhere.Models;

namespace SellEverywhere.Controllers
{   [Produces("application/json")]
    [Route("api/[controller]")]
    [ApiController]
    public class ListingsController : ControllerBase
    {
        private readonly ApplicationDbContext _context;

        public ListingsController(ApplicationDbContext context)
        {
            _context = context;
        }

        // GET: api/Listings
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Listing>>> GetListing()
        {
            return await _context.Listing.ToListAsync();
        }

        // GET: api/Listings/5
        [HttpGet("{id}")]
        public async Task<ActionResult<Listing>> GetListing(int id)
        {
            var listing = await _context.Listing.FindAsync(id);

            if (listing == null)
            {
                return NotFound();
            }

            return listing;
        }

        // PUT: api/Listings/5
        // To protect from overposting attacks, enable the specific properties you want to bind to, for
        // more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
        [HttpPut("{id}")]
        public async Task<IActionResult> PutListing(int id, Listing listing)
        {
            if (id != listing.Id)
            {
                return BadRequest();
            }

            _context.Entry(listing).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ListingExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // POST: api/Listings
        // To protect from overposting attacks, enable the specific properties you want to bind to, for
        // more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
        [HttpPost]
        public async Task<ActionResult<Listing>> PostListing(Listing listing)
        {
            listing.UserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            _context.Listing.Add(listing);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetListing", new { id = listing.Id }, listing);
        }

        // DELETE: api/Listings/5
        [HttpDelete("{id}")]
        public async Task<ActionResult<Listing>> DeleteListing(int id)
        {
            var listing = await _context.Listing.FindAsync(id);
            if (listing == null)
            {
                return NotFound();
            }

            _context.Listing.Remove(listing);
            await _context.SaveChangesAsync();

            return listing;
        }

        private bool ListingExists(int id)
        {
            return _context.Listing.Any(e => e.Id == id);
        }
    }
}

这是我的模型:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;


namespace SellEverywhere.Models
{
    public class Listing
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Key]
        public int Id { get; set; }
        public string UserId { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Color { get; set; }
        public string Condition { get; set; }
        public string Brand { get; set; }
        public string Type { get; set; }
        public string Size { get; set; }
        public string Tag1 { get; set; }
        public string Tag2 { get; set; }
        public string Tag3 { get; set; }
        public int Price { get; set; }
        public int LowestPrice { get; set; }
        public int ShippingPrice { get; set; }
        public ICollection<Photos> Photos { get; set; }
    }
    public class Photos
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Key]
        public int Id { get; set; }
        //[ForeignKey("Listing")]
        public int ListingId { get; set; }
        public byte[] Image { get; set; }
        public virtual Listing Listing { get; set; }
    }
}

【问题讨论】:

  • 可以分享一下型号相关代码SellEverywhere.ModelsSellEverywhere.Data;吗?另外,我建议你可以使用浏览器的F12开发工具,看看你第一次调用web api时发生了什么。

标签: c# asp.net-core-webapi blazor razor-pages blazor-server-side


【解决方案1】:

“提交”按钮在执行时会将表单数据提交到服务器,对吗?这是你的意图吗?我猜不是......您实际上想要调用 AddListing 方法,您可以从该方法执行对 Web Api 端点的 HTTP Fetch API(HttpClient 服务)调用,而不是执行表单数据的传统发布请求,这不会存在于SPA应用领域。因此,您应该使用不执行发布请求的类型的按钮,而只调用您的 AddListing 方法...

注意:在 Blazor 开发的早期阶段,Blazor JS 代码包含简单地取消“提交”请求的代码。

您应该使用 EditForm 组件和 Blazor Forms 组件(例如 InputText 等)来完成类似您的任务。在这种情况下,您可以使用“提交”按钮,其提交操作会被框架自动取消。

【讨论】:

  • 因此,在将按钮类型更改为“按钮”后,它现在可以在第一次尝试时使用。我仍然有兴趣知道为什么它只有在第一次尝试之后才会起作用。另外,如果常规 HTML 表单可以正常工作,为什么还要使用 EditForm 和 Blazor Form 组件?有什么优势/区别?谢谢!
  • 除非我看到您使用的完整代码,否则我无法准确说出发生了什么。然而,事实是单击“提交”按钮首先会启动服务器端发布,之后会发生其他事件处理程序。同样,根据您使用的代码,这可以有很多变化。但最重要的是要记住,提交按钮会执行服务器端发布,这是您不打算的,并且会导致重新创建您的 SPA。
  • "如果常规 HTML 表单可以正常工作,为什么还要使用 EditForm 和 Blazor 表单组件"。您不必使用内置的 Input* 组件,除非您想提供修改通知、验证和其他服务。创建这些组件是为了让开发人员的生活更轻松,让他可以专注于开发他的应用程序,而不是尝试创建具有内置组件提供的功能的组件和对象。
  • 感谢您提供的信息。我决定使用 Blazor InputFile 方法,现在我有一个新问题,如果你想看看:stackoverflow.com/questions/65460578/…
【解决方案2】:

把它放在 try catch 块中,有时它会抛出异常而不被注意到或显示。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-21
    • 2020-02-11
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 2020-01-05
    • 2021-04-09
    相关资源
    最近更新 更多