Reputation: 1739
I have been working on a project at work where I do not run into many null warnings and it is ASP.NET Core 5 I believe however I started a new project recently and decided to go with ASP.NET Core 7 and I am not sure why but I keep running into warnings related to nullable (CS8602 typically). I now find myself having to add if then statements to check if the variable is null or not. What am I doing wrong in this new version of .net? Its increasing my code side susbtantially I feel like. I have included a sample excerpt of my code below and as you will see if I removed a if(blah == null) then usage of that object would then become a warning for possible null. This snippet of code is for a contact us form and to implement a recaptcha on the front end (aka the token).
namespace Chesapeake_Tees_API.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ContactUsController : ControllerBase
{
private readonly ILogger<ContactUsController> _logger;
private readonly IEmailService _emailService;
private readonly string? reCaptchaKey = "";
public ContactUsController(IConfiguration configuration, ILogger<ContactUsController> logger, IEmailService emailService)
{
_logger = logger;
_emailService = emailService;
reCaptchaKey = configuration["CFTunrstileSecretKey"];
}
[HttpPost(Name = "PostContactUs")]
[EnableRateLimiting("api")]
public async Task<IActionResult> Post([FromBody] CustomerInquiry customerInquiry)
{
try
{
if (reCaptchaKey != null && customerInquiry.Token != null)
{
var dictionary = new Dictionary<string, string>
{
{ "secret", reCaptchaKey },
{ "response", customerInquiry.Token }
};
var postContent = new FormUrlEncodedContent(dictionary);
HttpResponseMessage? recaptchaResponseHTTP = null;
string? reCaptchaResponseJSON;
// Call recaptcha api and validate the token
using (var http = new HttpClient())
{
recaptchaResponseHTTP = await http.PostAsync("https://challenges.cloudflare.com/turnstile/v0/siteverify", postContent);
reCaptchaResponseJSON = await recaptchaResponseHTTP.Content.ReadAsStringAsync();
}
var reCaptchaResponse = JsonSerializer.Deserialize<ReCaptchaResponse>(reCaptchaResponseJSON);
if (recaptchaResponseHTTP.IsSuccessStatusCode)
{
if (reCaptchaResponse == null)
{
return Ok($"Message: Error code 503, unable to verify reCaptcha token");
}
else
{
if (reCaptchaResponse.Success)
{
EmailMessage emailMessage = new EmailMessage();
emailMessage.From = new Contact { DisplayName = "Chesapeake Tees", EmailAddress = "[email protected]" };
emailMessage.To.Add(new Contact { DisplayName = "Chesapeake Tees", EmailAddress = "[email protected]" });
emailMessage.ReplyTo = new Contact { DisplayName = customerInquiry.FirstName + " " + customerInquiry.LastName, EmailAddress = customerInquiry.EmailAddress };
emailMessage.Subject = "Custome Inquiry";
emailMessage.Body = customerInquiry.Message;
await _emailService.SendEmailAsync(emailMessage);
return Ok("Message: Success - email sent");
}
else
{
string? errors = null;
if (reCaptchaResponse.ErrorCodes != null)
{
errors = string.Join(",", reCaptchaResponse.ErrorCodes);
}
return Ok($"Message: Error code 500, {errors}");
}
}
}
else
{
return Ok($"Message: Error code 503, unable to verify reCaptcha token");
}
}
else
{
return Ok($"Message: Error code 503, unable to verify reCaptcha token");
}
}
catch (Exception ex)
{
_logger.LogInformation("Message: Error: " + ex.ToString(), DateTime.UtcNow.ToLongTimeString());
throw;
}
}
}
The model I am using which I have included ? to make them nullable (setting a default value to something doesnt seem to solve my warnings for the parent class itself but maybe the one property) includes the following:
public class ReCaptchaResponse
{
[JsonPropertyName("success")]
public bool Success { get; set; }
[JsonPropertyName("cdata")]
public string? CData { get; set; }
[JsonPropertyName("challenge_ts")]
public DateTime ChallengeTs { get; set; } // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
[JsonPropertyName("hostname")]
public string? HostName { get; set; } // the hostname of the site where the reCAPTCHA was solved
[JsonPropertyName("error-codes")]
public string[]? ErrorCodes { get; set; }
}
public class CustomerInquiry
{
[Required]
public string FirstName { get; set; } = "";
[Required]
public string LastName { get; set; } = "";
[Required]
[Phone]
[RegularExpression(@"^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$")]
public string PhoneNumber { get; set; } = "";
[Required]
[EmailAddress]
public string EmailAddress { get; set; } = "";
[Required]
public string Message { get; set; } = "";
[Required]
public string Token { get; set; } = "";
}
Upvotes: 1
Views: 2438
Reputation: 142273
You can disable NRTs (see the docs) either completely (<Nullable>disable</Nullable>
in csproj) or partially (#nullable disable
)
Do not specify the empty strings for properties, you can use the new required
keyword which I would argue is a nicer alternative:
public class CustomerInquiry
{
[Required]
public required string FirstName { get; set; }
// ...
}
customerInquiry.Token != null
is not needed from nullable analysis standpoint since Token
is declared as non-nullable.
Do not declare variables as nullable when not needed:
HttpResponseMessage recaptchaResponseHTTP;
string reCaptchaResponseJSON;
// Call recaptcha api and validate the token
using (var http = new HttpClient())
{
// PostAsync returns non-nullable HttpResponseMessage wrapped in task
recaptchaResponseHTTP = await http.PostAsync("https://challenges.cloudflare.com/turnstile/v0/siteverify", postContent);
// ReadAsStringAsync returns non-nullable string wrapped in task
reCaptchaResponseJSON = await recaptchaResponseHTTP.Content.ReadAsStringAsync();
}
recaptchaResponseHTTP.ToString(); // no warning
Also as I wrote in the comment - do not instantiate http clients manually, use DI with IHttpClientFactory
which will make the using obsolete and you can use inline assignment.
if (reCaptchaResponse.Success)
{
// ...
}
else
{
string errors = string.Join(",", reCaptchaResponse.ErrorCodes ?? new List<...>());
return Ok($"Message: Error code 500, {errors}");
}
private readonly string reCaptchaKey = "";
public ContactUsController(IConfiguration configuration, ILogger<ContactUsController> logger, IEmailService emailService)
{
var captchaKey = configuration["CFTunrstileSecretKey"];
ArgumentException.ThrowIfNullOrEmpty(captchaKey);
reCaptchaKey = captchaKey;
}
Upvotes: 2