【问题标题】:Variable not assigned to result fetch from URL in C#未分配给 C# 中从 URL 获取结果的变量
【发布时间】:2023-01-08 22:28:06
【问题描述】:

我遇到这个问题已经有一段时间了,所以为了重现错误,我创建了一个新的 Blazor 项目来向您展示所有文件。

这是我的index.razor 文件:

@using BlazorApp1.Models.TestSuiteModel; 
@inject BlazorApp1.Services.TestSuiteService TestSuiteServ; 
@page "/"


<b> 

    @TestSuitesResult.count; 

</b>



@code {

    protected override async Task OnInitializedAsync()
    {

        this.GetTestSuites(); 
    }


    TestSuiteModel TestSuitesResult;
    protected async Task GetTestSuites()
    {
        TestSuiteServ.url = "https://dev.azure.com/****/_apis/test/Plans/12/suites?api-version=5.0";
        TestSuiteServ.PersonalAccessToken = "****";
        TestSuitesResult = await TestSuiteServ.GetTestSuites();
        await base.OnInitializedAsync();

    }

}

问题是 @TestSuitesResult.count; 始终为 null 并抛出此错误:

下面是我的模型

namespace BlazorApp1.Models.TestSuiteModel
{
    public class TestSuiteModel
    {
        public List<Value> value { get; set; }
        public int count { get; set; }
    }
 // .. mode classes
}

以下是我的服务

using BlazorApp1.Models.TestSuiteModel;
using Newtonsoft.Json;
using System.Net.Http.Headers;

using System.Net.Http.Headers;

namespace BlazorApp1.Services
{
    public class TestSuiteService
    {
        public string url { get; set; }
        public string PersonalAccessToken { get; set; }
        TestSuiteModel result;

        public async Task<TestSuiteModel> GetTestSuites()
        {
            using (HttpClient client = new HttpClient())
            {
                // accept response as JSON
                client.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/json")
                );

                // add DevOps token to the HTTP Header request
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
                    "Basic", Convert.ToBase64String(
                        System.Text.Encoding.ASCII.GetBytes(
                            string.Format("{0}:{1}", "", PersonalAccessToken)
                        )
                    )
                );


                HttpResponseMessage response = await client.GetAsync(url);
                response.EnsureSuccessStatusCode();
                string responseData = await response.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject<TestSuiteModel>(responseData);

            }
        }
    }
}

这是我在这个项目中唯一拥有的东西。除了在 program.cs 中注册服务外,我没有修改或删除默认 Blazor 项目中的任何文件

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddSingleton<TestSuiteService>(); <!-- my service -->

var app = builder.Build();

还添加了 Newtonsoft,因此它不会是缺少的库。

【问题讨论】:

  • 除了答案中突出显示的 await 问题外,TestSuitesResult 可以为空,但您在尝试显示它之前不检查空值。麻烦的秘诀!你启用了 Nullable 吗?你应该收到警告。
  • @MrCakaShaunCurtis 这只是我创建的快速演示项目,通常我会检查 null
  • @page 移至文档顶部。

标签: c# blazor


【解决方案1】:

GetTestSuites 永远不会等待

protected override async Task OnInitializedAsync()
{

    this.GetTestSuites(); 
}

它应该是 :

protected override async Task OnInitializedAsync()
{

    await this.GetTestSuites(); 
}

现在 OnInitializedAsync 完成并且页面在 GetTestSuites 有机会完成之前呈现。

TestSuiteService 也有其他问题。 HttpClient 旨在被重用,而不是在 using 块中定义。特别是在 Blazor 中,HttpClient 实例是由浏览器提供的。整个GetTestSuites 方法可以替换为单个await _httpClient.ReadAsJsonAsync&lt;TestSuiteModel&gt;(url);,其中_httpClient 可以是通过AddHttpClient 注册的HttpClient。其他一切都是ReadAsJsonAsync的默认行为:

public interface ITestSuiteService
{
    Task<TestSuiteModel> GetTestSuites(string url);
}

public class TestSuiteService:ITestSuiteService
{
    public TestSuiteService(HttpClient client)
    {
        _httpClient=client;
    }

    public async Task<TestSuiteModel> GetTestSuites(string url)
    {
        var model=await _httpClient.ReadAsJsonAsync<TestSuiteModel>(url);
        return model;
    }
}

此服务可以注册为类型化的 HttpClient :

builder.Services.AddHttpClient<ITestSuiteService,TestSuiteService>(client=>{
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
                "Basic", Convert.ToBase64String(
                    System.Text.Encoding.ASCII.GetBytes(
                        $":{PersonalAccessToken}")
                    )
                )
            );
});

此时可以从配置中检索令牌:

【讨论】:

  • 感谢你的回答。我将 await 添加到方法 this.GetTestSuites(); 中,但我仍然遇到相同的错误。我会在解决问题后添加其他建议,因为它们是改进。我仍然收到 System.NullReferenceException: 'Object reference not set to an instance of an object.' TestSuitesResult was null. 错误
  • 它们不仅仅是改进。所有这些额外的代码增加出错的机会。所有这些代码都可以用一行替换。它是仍然如果响应为空,则可能会得到 NULL。您是否尝试调用该 URL 并检查响应?
  • 尝试创建一个单元测试,只调用带有 ReadAsJsonAsync 的 URL。调试单个调用比调试整个 UI 页面要容易得多
  • 我按照你的指示做了,但我现在收到一个新错误:i.imgur.com/uIqH9bf.png 你能检查一下吗?谢谢
  • 您需要添加using System.Net.Http.Json;。我不是ReadAsJsonAsync 的忠实粉丝。你得到你的对象或一个空值。没有状态信息可以向用户/UI 提供有意义的信息。 null 可能是没有数据或错误的 url 或未授权,....?
【解决方案2】:

在 blazor 6 中,您应该始终检查您的对象值是否为 null,因为页面总是在 lyfeevents 执行之前预先呈现。 是唯一的办法。 不是您的应用程序问题,只是 Blazor 错误。

在 .net7 中使用@bind-after new 标记解决了此错误。 https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-preview-7/

【讨论】:

    猜你喜欢
    • 2018-08-30
    • 1970-01-01
    • 1970-01-01
    • 2021-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-16
    • 1970-01-01
    相关资源
    最近更新 更多