Ivan
Ivan

Reputation: 193

PutAsync() method call failing in Blazor WebAssembly project, yet debugging not working well enough to see what's going on

I've been trying to learn Blazor WebAssembly programming and have been doing a follow-along project along with the Pluralsight course I'm working on.

What happens is that the PutAsync() method in the following code fails:

        public async Task UpdateEmployee(Employee employee)
        {
            try
            {
                var jsonString = JsonSerializer.Serialize(employee).ToString();
                var employeeJson =
                    new StringContent(JsonSerializer.Serialize(employee), Encoding.UTF8, "application/json");
                await _httpClient.PutAsync($"/api/employee", employeeJson);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }

        }

This code is located in a service class, and the target method that has the HttpPut attribute either doesn't seem to get called, or the breakpoint I place in it never gets hit.

  [HttpPut]
        public IActionResult UpdateEmployee([FromBody] Employee employee)
        {
            if (employee == null)
                return BadRequest();

            if (employee.FirstName == string.Empty || employee.LastName == string.Empty)
            {
                ModelState.AddModelError("Name/FirstName", "The name or first name shouldn't be empty");
            }

            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            var employeeToUpdate = _employeeRepository.GetEmployeeById(employee.EmployeeId);

            if (employeeToUpdate == null)
                return NotFound();

            _employeeRepository.UpdateEmployee(employee);

            return NoContent(); //success
        }

I put the breakpoint on the if (employee == null) line, but it doesn't get hit. I initially had a lot of problems with breakpoints not being hit, but ever since I switched from Firefox to Chrome for my testing, it's been a lot better. Yet, the execution doesn't seem to get here. The routing attributes at the top of this target class looks like the following:

 [Route("api/[controller]")]
 [ApiController]

The class itself is called EmployeeController, so I would think that the "api/employee" attribute should reach it.

I've added a try/catch block around the failing code in the first code block I've posted, and when I step through, I see that there's an exception that gets thrown after I try to step over the PutAsync() method. But when I go to command window to se what the content of ex.Message is, I get the "Unable to Evaluate" error in Visual Studio.

enter image description here

If I step through, I see the execution hitting some bound fields in the Razor file that displays the edit form, but I don't know enough about Razor to really deduce what is going on. There are numerous fields in there that look like this:

   <div class="form-group row">
            <label for="lastName" class="col-sm-3">Last name: </label>
            <InputText id="lastName" @bind-Value="@Employee.LastName" class="form-control col-sm-8" placeholder="Enter last name"></InputText>
        </div>

        <div class="form-group row">
            <label for="firstName" class="col-sm-3">First name: </label>
            <InputText id="firstName" class="form-control col-sm-8" @bind-Value="@Employee.FirstName" placeholder="Enter first name"></InputText>
        </div>

        <div class="form-group row">
            <label for="birthdate" class="col-sm-3">Birthdate: </label>
            <InputDate id="birthdate" class="form-control col-sm-8" @bind-Value="@Employee.BirthDate" placeholder="Enter birthdate"></InputDate>
        </div>

        <div class="form-group row">
            <label for="email" class="col-sm-3">Email: </label>
            <InputText id="email" class="form-control col-sm-8" @bind-Value="@Employee.Email" placeholder="Enter email"></InputText>
        </div>

And then, at some point, while stepping through, a lot of the fields get skipped, and the execution seems to leave the Razor file abruptly. I am not sure what to make of this, and since I have been largely absent from .Net for the last several years, it all does feel a bit overwhelming. I am not quite sure how the PutAsync() method call failure really translates into these possible data binding errors, but I am guessing that PutAsync() is just a vehicle to call whatever code has the [HttpPut] attribute, and that any kind of exception anywhere down the line from this method call will cause the PutAsync() call to fail. Would that be accurate or not?

Upvotes: 0

Views: 363

Answers (3)

Ivan
Ivan

Reputation: 193

Thank you all so much for all the helpful advice, especially the Browser Dev Tools console tip.

So, I changed the System.Diagnostics.Debug.WriteLine(ex.Message) line to Console(ex.Message), and the error I saw in the Browser Dev Tools console was:

Access to fetch at 'https://localhost:44340/api/employee' from origin 'https://localhost:44362' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

I went to the ConfigureServices() method in the startup.cs file of the web service, and I changed the code from

    services.AddCors(options =>
            {
                options.AddPolicy("Open", builder => builder.AllowAnyOrigin().AllowAnyHeader());
            });

to

            services.AddCors(options =>
            {
                options.AddPolicy("Open", builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
            });

The error is gone, and the employee record gets saved correctly now. I do realize that this isn't exactly a sound programming practice to call something like AllowAnyMethod(), but for the purposes of this course, it's sufficient for now. Thank you all again!

Upvotes: 1

Nb777
Nb777

Reputation: 2032

  1. Don't use Built-in form components without EditForm and model or EditContext.
  2. Use DataAnnotationsValidator and/or ValidationSummary component to validate your form before submitting.
  3. use OnValidSubmit method to submit your form only when has a validate controls.
  4. If you don't want to use OnValidSubmit, check if your EditContext validate before request PutAsync().
  5. Please check Microsoft doc for more information, I am sure that is more clear than your course that you are following.

Upvotes: 1

spacemonki
spacemonki

Reputation: 303

try to await "api/employee"

var employeeToUpdate = await _employeeRepository.GetEmployeeById(employee.EmployeeId);

Upvotes: 1

Related Questions