I am trying to upload a file using webapi (.NET6 ). There is no errors in the code . But warnings are there.I have getting below messge in terminal when I run the .csproj file.
warning CS8618: Non-nullable property 'files' must contain a non-null value
when exiting constructor. Consider declaring the property as nullable.
While I runs, Getting this message and running stops. Warning shows in fileModel.cs at files
I have tried putting IFormFile?. still showing warnings and not runs.
I'm Using VS Code. wwwroot folder is created.
My file model:
fileModel.cs
{
public class fileModel
{
public IFormFile files {get; set;}
}
}
Controller file
FilesController.cs
public class FilesController : ControllerBase
{
[HttpPost]
public string UploadFile(fileModel objFile)
{
try
{
if (objFile.files.Length > 0)
{
UploadFile(objFile);
return "Upload" + objFile.files.FileName;
}
else
{
return "Failed";
}
}
catch (System.Exception ex)
{
return ex.Message.ToString();
}
}
}
Interface & Implementation:
IFileUpload.cs:
public interface IFileUpload
{
public void UploadFile(fileModel formFile);
}
FileUpload.cs
public class FileUpload : IFileUpload
{
private IWebHostEnvironment environment;
public FileUpload(IWebHostEnvironment _enviornment)
{
environment = _enviornment;
}
//
public void UploadFile(fileModel formFile){
if (!Directory.Exists(environment.WebRootPath + "\\Uploads"))
{
Directory.CreateDirectory(environment.WebRootPath + "\\Uploads");
}
using(FileStream fileStream = System.IO.File.Create(environment.WebRootPath + "\\Uploads"+formFile.files.FileName)){
formFile.files.CopyTo(fileStream);
fileStream.Flush();
}
}
}
While I runs, Getting this message and running stops. Warning shows in
fileModel.cs at files.
Well, its pretty obvious that it will stop suddenly because you haven't initialize your IFileUpload interface into your controller class therefore, it will certainly break the execution. You ought to write that in following manners:
Controller With Constructor:
public class FilesController : Controller
{
private readonly IFileUpload _fileUplaod;
public FilesController(IFileUpload fileUplaod)
{
_fileUplaod = fileUplaod;
}
[HttpPost]
public IActionResult UploadFile(fileModel objFile)
{
try
{
if (objFile.files.Length > 0)
{
_fileUplaod.UploadFile(objFile);
return Ok("Upload" + objFile.files.FileName);
}
else
{
return Ok("Failed");
}
}
catch (System.Exception ex)
{
return Ok(ex.Message.ToString());
}
}
}
Program.cs
You must register above interface on your program.cs file as following
builder.Services.AddScoped<IFileUpload, FileUpload>();
Output
Execution Debugging Result:
According to Microsoft's recommendation, throwing and catching should not be used for the normal logic of the program.
Minimize exceptions
As part of a ASP.Net core clean architecture project (with 3 Layers Generic Repositories - BL Services - Controllers), how should the error handling and the results be designed and implemented?
Should a struct or a global result class be used for all Api Controllers and BL services?
Is it enough if the errors and the results are encapsulated in a struct?
Example of result class in the WebApi project:
public class ExampleResult<T>
{
public ExampleResult(T value, string message, bool success)
{
(...)
}
}
Controller:
public ActionResult<ExampleResult<NewResourceDto>> Post([FromBody] NewResourceDto myNewResource)
{
try
{
if(!Validate(myNewResource))
return new ExampleResult(null, "some business logic validate failed", true);
ExampleResult result = _service.TrySaveMyNewResource(myNewResource);
return result;
}
catch (Exception ex)
{
// Log the exception here...
return new ExampleResult(null, "some message" + ex, false);
}
}
The Angular Client then validates if the value is null and/or whether the success is true or false.
The message contains the error messages.
The http status will be 200 (no matter if success or not).
How are the exceptions minimized elegantly?
targeting the best practice in .Net Core or any other framework you need to return a common model of all of your apis that holds all the date returned from your api in case if it's a result or an error then in your angular service you should check on your returned object keys which is your base model.
public class ErrorModel
{
public ErrorModel()
{
ErrorMessages = new List<string>();
}
public List<string> ErrorMessages { get; set; }
public Exception Exception { get; set; }
}
public class BaseModel
{
public BaseModel()
{
Error = new ErrorModel();
}
public ErrorModel Error { get; set; }
}
public class BaseModel<T>: BaseModel
{
public BaseModel()
{
Error = new ErrorModel();
}
public bool HasError => Error.ErrorMessages.Count > 0 || Error.Exception != null;
public T Result { get; set; }
}
then your api should look like that
public ActionResult<BaseModel<dynamic>> Post([FromBody] NewResourceDto myNewResource)
{
try
{
ExampleResult result = _service.TrySaveMyNewResource(myNewResource);
return OK( new BaseModel<dynamic>()
{
Result=result
});
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, new BaseModel<dynamic>()
{
Error = new ErrorModel()
{
ErrorMessages = new List<string>()
{
ex.Message,
"your message 2",
"your message 3"
},
Exception = ex
}
});
}
}
then in your angluar service you shold check on your response.hasError and displays your data according to it.
I agree that throwing Exceptions should not be used as signaling in the system. Maybe I don't understand your question about the returning a struct or a global result class. Seems like a bad idea. Especially, don't return HTTP OK 200 if something goes south.
Keep your Web API controllers as thin and dumb as possible
Wrap your Web API controller method methods in a try-catch so you always return HTTP Internal Server Error 500 on an unexpected error
Example of a controller method:
public IActionResult Post([FromBody] NewResourceDto myNewResource)
{
try
{
_service.TrySaveMyNewResource(myNewResource);
return StatusCode(201);
}
catch (Exception ex)
{
// Log the exception here...
return StatusCode(500);
}
}
I have a asp.net core web api. As of now I'm using ILogger to log the messages. But ILogger doesn't have Fatal loglevel in it. There is Critical level, but our team requires Fatal word instead of Critical word.Is there any way I can tweak the work which gets printed to logs?
If not, I want to replace ILogger with log4Net which has Fatal level in it.So this is what I have done , but somehow it is not working.
I have multi layer architecture : WebApplication1, WebApplication1.Helper . All these are different projects with in a solution.
In WebApplication1:
I have added Microsoft.Extensions.Logging.Log4Net.AspNetCore reference.
In startup.cs
public void ConfigureServices(IServiceCollection apiServices)
{
var provider = apiServices.BuildServiceProvider();
var factory = new LoggerFactory()
.AddConsole().AddLog4Net().AddApplicationInsights(provider, LogLevel.Information);
apiServices.AddSingleton(factory);
apiServices.AddLogging();
apiServices.AddMvc();
apiServices.AddOptions();
}
HomeController.cs
[Route("api/[controller]")]
[ApiController]
public class HomeController : Controller
{
private readonly ILog4NetHelper _logHelper = new Log4NetHelper();
[HttpGet]
public virtual IActionResult GetData()
{
try
{
_logHelper.Log4NetMessage("Info", "Start GetData");
return new OkObjectResult("Your in Home Controller");
}
catch (Exception ex)
{
_logHelper.Log4NetMessage("Error", "Exception in GetData" + ex.Message);
throw;
}
}
}
WebApplication1.Helper project
And in WebApplication1.Helper project , I have added a interface ILog4NetHelper and class which implements this interface Log4NetHelper. Also I have added log4Net config file.
public class Log4NetHelper : ILog4NetHelper
{
readonly ILog _log =log4net.LogManager.GetLogger(typeof(Log4NetHelper));
public void Log4NetMessage(string type,string message)
{
string logMessage = message;
switch (type)
{
case "Info":
_log.Info(logMessage);
break;
case "Error":
_log.Error(logMessage);
break;
case "Fatal":
_log.Fatal(logMessage);
break;
default:
_log.Info(logMessage);
break;
}
}
}
When I host this application and run this, it is giving me a 500 internal server error. The error message is this :
InvalidOperationException: Unable to resolve service for type
'WebApplication1.Helper.Log4NetHelper' while attempting to activate
'WebApplication1.Helper.Log4NetHelper'.
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type
serviceType, Type implementationType, CallSiteChain callSiteChain,
ParameterInfo[] parameters, bool throwIfCallSiteNotFound)
How can I resolve this?
ASP.Net Core built-in logging was Microsoft's stab at doing logging the Microsoft, dependency-injected way. It follows the basic principles and tenets of the Log4Net approach (which has been standardized across .Net, Java, and Javascript, among others). So, the two approaches are not entirely at odds with one another.
However, in this particular case, the implementation appears to actually conflict with the intent of both approaches to logging.
Log4Net separates out the two acts of recording and writing log output. The first is done via the ILog interface. The second is done via one of the Appenders.
Similarly, the ASP.net Core API uses an ILogger and one or more Providers to emit log messages.
As I am more comfortable with log4net, and also don't see much of a point in having loggers added via dependency injection in EVERY CLASS, I used log4net's approach of LogManager.GetLogger(typeof(MyClass)) rather than doing it via Microsoft DI. My appenders also run through log4net. Thus, my implementation focused on translating the Microsoft logging outputs into the log4net format, which appears to be the what your team would like but the opposite of what you are doing here. My approach was based on this article. The code I used is below.
Implementation Notes:
I set up a custom appender via log4net which writes my logs out to a logging database (commonly-used databases for this are loki and/or elasticsearch).
In the Configure() method on startup.cs, you'll need to have the following line (note that I instantiate the customAppender in the ConfigureServices and then add it to the DI, but you wouldn't have to do it this way):
loggerFactory.AddLog4Net(_serverConfig.LoggingSettings, customAppender);
It is also necessary to have the following in ConfigureServices() (not sure why, but it seems to ensure that the regular .net core logging kicks in).
services.AddLogging(config => {
config.AddDebug();
config.AddConsole();
});
Log4NetLogger.cs
/// <summary>
/// Writes ASP.net core logs out to the log4net system.
/// </summary>
public class Log4NetLogger : ILogger
{
private readonly ILog _logger;
public Log4NetLogger(string name)
{
_logger = LogManager.GetLogger(typeof(Log4NetProvider).Assembly, name);
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
switch (logLevel) {
case LogLevel.Critical:
return _logger.IsFatalEnabled;
case LogLevel.Debug:
case LogLevel.Trace:
return _logger.IsDebugEnabled;
case LogLevel.Error:
return _logger.IsErrorEnabled;
case LogLevel.Information:
return _logger.IsInfoEnabled;
case LogLevel.Warning:
return _logger.IsWarnEnabled;
default:
throw new ArgumentOutOfRangeException(nameof(logLevel));
}
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
{
if (!this.IsEnabled(logLevel)) {
return;
}
if (formatter == null) {
throw new ArgumentNullException(nameof(formatter));
}
string message = null;
if (null != formatter) {
message = formatter(state, exception);
}
if (!string.IsNullOrEmpty(message) || exception != null) {
switch (logLevel) {
case LogLevel.Critical:
_logger.Fatal(message);
break;
case LogLevel.Debug:
case LogLevel.Trace:
_logger.Debug(message);
break;
case LogLevel.Error:
_logger.Error(message);
break;
case LogLevel.Information:
_logger.Info(message);
break;
case LogLevel.Warning:
_logger.Warn(message);
break;
default:
_logger.Warn($"Encountered unknown log level {logLevel}, writing out as Info.");
_logger.Info(message, exception);
break;
}
}
}
Log4NetProvider.cs
/// <summary>
/// Returns new log4net loggers when called by the ASP.net core logging framework
/// </summary>
public class Log4NetProvider : ILoggerProvider
{
private readonly LoggingConfig _config;
private readonly ConcurrentDictionary<string, Log4NetLogger> _loggers =
new ConcurrentDictionary<string, Log4NetLogger>();
private readonly ILoggerRepository _repository =
log4net.LogManager.CreateRepository(typeof(Log4NetProvider).Assembly, typeof(log4net.Repository.Hierarchy.Hierarchy));
public Log4NetProvider(LoggingConfig config, MyCustomAppender otherAppender)
{
_config = config;
BasicConfigurator.Configure(_repository, new ConsoleAppender(), otherAppender);
LogManager.GetLogger(this.GetType()).Info("Logging initialized.");
}
public ILogger CreateLogger(string categoryName)
{
return _loggers.GetOrAdd(categoryName, this.CreateLoggerImplementation(categoryName));
}
public void Dispose()
{
_loggers.Clear();
}
private Log4NetLogger CreateLoggerImplementation(string name)
{
return new Log4NetLogger(name);
}
}
Log4NetExtensions.cs
/// <summary>
/// A helper class for initializing Log4Net in the .NET core project.
/// </summary>
public static class Log4netExtensions
{
public static ILoggerFactory AddLog4Net(this ILoggerFactory factory, LoggingConfig config, MyCustomAppender appender)
{
factory.AddProvider(new Log4NetProvider(config, appender));
return factory;
}
}
I ran into multiple issues with Log4Net few years ago - i don't remember exactly what, since then i switched to my own implementation for log, it is not complicated to write your own. my implementation is copied below.
When i had to change the log class, it was nightmare since i had to replace all its usage with the new implementation.
There are 2 benefits of custom class.
A. It will match your application's requirement.
B. It acts as a wrapper for any underlying logging framework that you may use. So now even if i have to change logging in future, i just have to change the internal implementation of this custom class.
Since writing this wrapper, i have changed from log4net to Microsofts log extensions and now 100 % my own implementation.
using System;
using System.Text;
using System.IO;
namespace BF
{
//This is a custom publisher class use to publish the exception details
//into log file
public class LogPublisher
{
private static object _lock;
static LogPublisher()
{
_lock = new object();
}
//Constructor
public LogPublisher()
{
}
//Method to publish the exception details into log file
public static void Debug(string message)
{
if (ClientConfigHandler.Config.IsDebugMode())
{
Exception eMsg = new Exception(message);
Publish(eMsg, "#DEBUG");
}
}
public static void DebugBackgroundAction(string message)
{
if (ClientConfigHandler.Config.IsDebugMode())
{
Exception eMsg = new Exception(message);
Publish(eMsg, "#DEBUG #BG");
}
}
public static void BackgroundAction(string message)
{
Exception eMsg = new Exception(message);
Publish(eMsg, "#BG");
}
public static void Publish(string message)
{
Exception eMsg = new Exception(message);
Publish(eMsg, "");
}
public static void Publish(Exception fException)
{
Publish(fException, "");
}
public static void Publish(Exception fException, string prefix)
{
if (fException == null) return;
// Load Config values if they are provided.
string m_LogName = ResourceConfig.LogFileName;
// Create StringBuilder to maintain publishing information.
StringBuilder strInfo = new StringBuilder();
// Record required content of the AdditionalInfo collection.
strInfo.AppendFormat("{0}**T {1} {2} ", Environment.NewLine, CommonConversions.CurrentTime.ToString(CommonConversions.DATE_TIME_FORMAT_LOG), prefix);
// Append the exception message and stack trace
strInfo.Append(BuildExceptionLog(fException, false));
try
{
lock (_lock)
{
FileStream fs = File.Open(m_LogName, FileMode.Append, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
sw.Write(strInfo.ToString());
sw.Close();
fs.Close();
}
}
catch
{
//ignore log error
}
}
private static string BuildExceptionLog(Exception fException, bool isInnerExp)
{
StringBuilder strInfo = new StringBuilder();
if (fException != null)
{
string msgType;
if (isInnerExp)
{
msgType = "#IN-ERR";
}
else if (fException.StackTrace == null)
{
msgType = "#INF";
}
else
{
msgType = "#ERR";
}
strInfo.AppendFormat("{0}: {1}", msgType, fException.Message.ToString());
if (fException.StackTrace != null)
{
strInfo.AppendFormat("{0}#TRACE: {1}", Environment.NewLine, fException.StackTrace);
}
if (fException.InnerException != null)
{
strInfo.AppendFormat("{0}{1}", Environment.NewLine, BuildExceptionLog(fException.InnerException, true));
}
}
return strInfo.ToString();
}
}
}
The Microsoft.Extensions.Logging system doesn't contain Fatal level as an accepted LogLevel.
But, if you are interested in treat the Critical messages as log4net's Fatal level, starting at v.2.2.5, there is a property on Microsoft.Extensions.Logging.Log4Net.AspNetCore nuget package that allow you to decide if the Critical Level is managed as Fatal or not.
Please, review the original issue to check why the implementation was done as it was.
I see one issue in your code. Your class Log4NetHelper requires in constructor an instance of Log4NetHelper. So it can't create Log4NetHelper. Why you passing Log4NetHelper in constructor, it smells. You should provide default constructor, or constructor with parameters which are registered in DI service.
Please try add parameterless constructor and check if it works, if not check exception and/or error message.
I am trying to add some middleware so that any unhandled exceptions I catch and log it but experiencing some difficulties in doing so. Not been able to find a lot on this and for some odd reason my code doesn't seem to be entering the catch block. Seems like it is gracefully handling this and even interrogating the dictionary I can't see the exception.
What I want to happen is, enter the catch block grab the exception and log the stack trace.
The code:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
app.Use(typeof(FooHandler));
app.UseWebApi(config);
}
}
public class FooHandler : OwinMiddleware
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(FooHandler));
public FooHandler(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
if (Logger.IsErrorEnabled)
{
try
{
await Next.Invoke(context);
}
catch (Exception ex)
{ // DOESN'T FALL INTO HERE!
Logger.Error(message, ex);
}
}
}
}
public class FooController : ApiController
{
public Task<IHttpActionResult> Get()
{
throw new Exception("Foo Bar");
}
}
This is because WebApi is handling the exception. You will need to handle exceptions thrown by Controllers in an ExceptionFilterAttribute
In my C# Web API, I'm trying to add a global exception handler. I've been using a custom global ExceptionFilterAttribute to handle the exception and return a HttpResponseMessage:
public override void OnException(HttpActionExecutedContext context)
{
...
const string message = "An unhandled exception was raised by the Web API.";
var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(message),
ReasonPhrase = message
};
context.Response = httpResponseMessage;
}
This has worked fine for handling exceptions thrown at the controller level.
However, during development we had an error thrown from our OWIN startup file due to a database connection issue, however, a standard IIS exception was returned, instead of going through the global exception handler, and the full HTML was returned to our API consumer.
I've tried a few different approaches to catch exceptions thrown in my OWIN startup:
Custom ApiControllerActionInvoker:
public class CustomActionInvoker : ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
var result = base.InvokeActionAsync(actionContext, cancellationToken);
if (result.Exception != null && result.Exception.GetBaseException() != null)
{
...
}
return result;
}
}
Custom ExceptionHandler:
public class CustomExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
...
base.Handle(context);
}
public override bool ShouldHandle(ExceptionHandlerContext context)
{
return true;
}
}
Custom OwinMiddleware component:
public class CustomExceptionMiddleware : OwinMiddleware
{
public CustomExceptionMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (Exception ex)
{
...
}
}
}
And finally just using Application_Error:
protected void Application_Error(object sender, EventArgs e)
{
...
}
But nothing seems to catch the exception.
Does anyone know of a way to catch the exception and return a HttpResponseMessage? Or if any of the approaches I've already tried should have worked?
Any help much appreciated.
I have an application that does this correctly. In my case I wrote a middleware class that always returns a message telling the caller that the service is unavailable because there was an error during startup. This class is called FailedSetupMiddleware in my solution. The outline of it looks like this:
public class FailedSetupMiddleware
{
private readonly Exception _exception;
public FailedSetupMiddleware(Exception exception)
{
_exception = exception;
}
public Task Invoke(IOwinContext context, Func<Task> next)
{
var message = ""; // construct your message here
return context.Response.WriteAsync(message);
}
}
In my Configuration class I have a try...catch block that configures the OWIN pipeline with only the FailedSetupMiddleware in the case where an exception was thrown during configuration.
My OWIN startup class looks like this:
[assembly: OwinStartup(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
try
{
//
// various app.Use() statements here to configure
// OWIN middleware
//
}
catch (Exception ex)
{
app.Use(new FailedSetupMiddleware(ex).Invoke);
}
}
}