.NET 6 - Adding Correlation ID from Middleware to Serilog File in ASP.NET Core Web API - asp.net-core-webapi

I have a .NET 6 based ASP.NET Core Web API. I am using Serilog to log the requests to the file log.
Below is my middleware class,
using AspNet.CorrelationIdGenerator;
using Microsoft.Extensions.Primitives;
using Serilog.Context;
namespace MyService.Middlewares.CorrelationId;
public class CorrelationIdHandler
{
private readonly RequestDelegate _next;
private const string _correlationIdHeader = "X-Correlation-Id";
public CorrelationIdHandler(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext context, ICorrelationIdGenerator correlationIdGenerator)
{
var correlationId = GetCorrelationId(context, correlationIdGenerator);
AddCorrelationIdHeaderToResponse(context, correlationId);
using (LogContext.PushProperty(_correlationIdHeader, correlationId))
{
await _next(context);
}
}
private static StringValues GetCorrelationId(HttpContext context, ICorrelationIdGenerator correlationIdGenerator)
{
if (context.Request.Headers.TryGetValue(_correlationIdHeader, out var correlationId))
{
correlationIdGenerator.Set(correlationId);
return correlationId;
}
else
{
return correlationIdGenerator.Get();
}
}
private static void AddCorrelationIdHeaderToResponse(HttpContext context, StringValues correlationId)
=> context.Response.OnStarting(() =>
{
context.Response.Headers.Add(_correlationIdHeader, new[] { correlationId.ToString() });
return Task.CompletedTask;
});
}
In the Swagger response, I get the following in response headers:
api-supported-versions: 1.0
content-type: application/json; charset=utf-8
date: Fri,19 Aug 2022 23:22:03 GMT
server: Kestrel
x-correlation-id: 1ff3d20e-e8a3-4cb3-a5ab-3b0f3c50b2fc
But, in the Serilog log file, I am not getting the expected CorrelationId. Log looks like following,
{
"Timestamp": "2022-08-19T23:22:04.1299119+00:00",
"Level": "Information",
"MessageTemplate": "{\"Id\":\"-1\"}",
"RenderedMessage": "{\"Id\":\"-1\"}",
"Properties": {
"MachineName": "VMA",
"ApplicationName": "MyService",
"Version": "1.0.0.0",
"EnvironmentName": "Development",
"SpanId": "01d9fefb3b3499f4",
"TraceId": "a6d9a772a7714262de644869a0db09ac",
"ParentId": "0000000000000000",
"ActionId": "38911c40-e519-465a-a5df-45cb0b8ddfa5",
"ActionName": "MyAction",
"RequestId": "0HMK275AMJ90H:0000000B",
"RequestPath": "/api/v1/users",
"ConnectionId": "0HMK275AMJ90H",
"EventType": "A4522030"
}
}

Related

Azure signalR + Azure http trigger - Authorize error when deployed

I have created an Azure SignalR (Serverless) reosurce in azure portal.
Then I have created an azure function HttpTrigger locally that references Microsoft.Azure.WebJobs.Extensions.SignalRService. In my azure function I have this code:
`public static class HttpTrigger
{
[FunctionName("Negotiate")]
public static SignalRConnectionInfo Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[SignalRConnectionInfo(HubName = "notificationHub")] SignalRConnectionInfo connectionInfo,
ILogger log)
{
log.LogInformation("Returning connection: " + connectionInfo.Url + "" + connectionInfo.AccessToken);
return connectionInfo;
}
[FunctionName("Notify")]
public static async Task<IActionResult> Notify([HttpTrigger(AuthorizationLevel.Function, "get", Route=null)] HttpRequest req,
[SignalR(HubName = "notificationHub")] IAsyncCollector<SignalRMessage> signalRMessage,
ILogger log)
{
log.LogInformation("Notify");
string msg = string.Format("Message from agent! {0} ", DateTime.Now);
await signalRMessage.AddAsync(
new SignalRMessage
{
Target = "notifications",
Arguments = new[] { msg }
});
return new OkObjectResult("ok");
}
}
`
Also in my azure function, this is what my local.settings.json looks like:
`
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"AzureSignalRConnectionString": "myconnstringhere"
},
"Host": {
"LocalHttpPort": 7071,
"CORS": "http://localhost:53377",
"CORSCredentials": true
}
}
To also solve the CORS problem, I have added http://localhost:53377 domain of my client part project.
My client part is a separate asp.net web application project . So here I am connecting to this azure function like this:
`
<script>
$(document).ready(function(){
const connection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:7071/api/")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.onclose(start);
start(connection);
});
async function start(connection){
try {
await connection.start();
console.log("SignalR connected.");
connection.on("notifications", (message) => {
$("#detailcontainer").html(message);
console.log(message)
});
}
catch(err) {
console.log(err);
}
}
</script>
Now, I have published my azure function. But now it is not working anymore. It gets an error saying unauthorized when triggering /api/negotiate.
My azure function is a .net 6 project while the client app is a net framework 4.8. Is this because my client app is still in webforms?
I have added the connection string of my azure signalR to the application settings having a name format like this: Azure__SignalR__ConnectionString
I also have configured CORS allowed origins for my azure function, I added my client localhost app.
Closing this one because I found the answer. And it was really annoying that I have missed this one out.
I replaced AuthorizationLevel.Function to AuthorizationLevel.Anonymous. Because I am just passing the domain/api of my azure function and letting the signalR do its thing on their JS.

ASP.Net Core WebApi projects in VS 2022: 404 when renaming controller

I rarely dive into web development and was using the following link to build a small project: https://learn.microsoft.com/en-us/visualstudio/javascript/tutorial-asp-net-core-with-angular?view=vs-2022
Using this project I've gone to rename the controller and now when loading the page the request returns a 404. If I name it back the page loads as expected (in the three components below by renaming Products to WeatherForecast). The 3 places I re-named are below. This is still using dummy data as I learn angular.
What am I missing by doing a simple rename here?
product.component.ts:
export class ProductComponent implements OnInit {
public forecasts?: Products[];
constructor(http: HttpClient) {
http.get<Products[]>('/products').subscribe(result => {
this.forecasts = result;
}, error => console.error(error));
}
proxy.conf.js:
const PROXY_CONFIG = [
{
context: [
"/products",
],
target: "https://localhost:7050",
secure: false
}
]
module.exports = PROXY_CONFIG;
Products controller:
using Microsoft.AspNetCore.Mvc;
namespace WebApplication1.Controllers
{
[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<ProductsController> _logger;
public ProductsController(ILogger<ProductsController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetProducts")]
public IEnumerable<Products> Get()
{
return Enumerable.Range(1, 5).Select(index => new Products
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
404:
According to your HttpErrorResponse, you are not sending request to .NET API, but to localhost:4200 which I believe is your Angular application.
Try sending request like this:
http.get<Products[]>('https://localhost:7050/products').subscribe...
If that works, your proxy configuration is messing with the request. Be sure to get the port right and to start Angular application after .NET application has started. You can check your ports in launchSettings.json, be careful because http and https use different ports. Use http if your certificates are not configured.
Don't forget to include proxy.conf.json in angular.json under "serve" like this:
"architect": {
"serve": {
"builder": "#angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.json"
},
To extend #Ocram's answer - which is absolutely correct and relevant -, I also found that if you have a context: [] list in your PROXY_CONFIG definition in the proxy.conf.js file (the list of paths/controllers you want to redirect to the backend port), then you need to ensure that this list contains all the controller names you need.
It is because when you rename a controller, you also need to inform the client proxy config about the change.
const PROXY_CONFIG = [
{
///-----------------
context: [
"/weatherforecast",
"/test",
"/mynewapi",
],
///-----------------
changeOrigin: true,
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
}
]

Log to Console with .Net Core 2.0 web application

I'm giving my first steps with .Net Core
Just created a web Hello world with
dotnet new web
I can see there's some kind of logging enabled. I just want to log something to the Console.
But I don't know how to access the logger from
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!!!");
});
I tried with Console.WriteLine but it obviously didn't work.
Also tried with NLog following this guide https://github.com/NLog/NLog.Web/wiki/Getting-started-with-ASP.NET-Core-(csproj---vs2017) but I don't know how to inject the logger.
I'm just trying to look around for educational purposes, not looking for a real logger, so perhaps there's a better/easier option.
I could achieve it with this:
[...]
using Microsoft.Extensions.Logging;
[...]
namespace web
{
public class Startup
{
ILogger log;
public Startup(ILoggerFactory loggerFactory)
{
log = loggerFactory.CreateLogger("Logger");
}
[...]
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
loggerFactory.AddDebug();
[...]
app.Run(async (context) =>
{
log.LogInformation("logging!");
await context.Response.WriteAsync("Hello World!");
});
}
}
}
also had to add an appsettings.json file to the root of the project
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

Swagger Bearer always return 501 with good token in ASP Core Web Api

In my web API I'm using swagger. Everything worked fine, but since 5 hours there is no way to execute a request to my API from swagger, it always returns me
{
"access-control-allow-origin": "*",
"date": "Wed, 21 Jun 2017 12:20:52 GMT",
"www-authenticate": **"Bearer error=\"invalid_token\", error_description=\"The
token is expired\""**,
"x-sourcefiles": "=?UTF-8?B?pcdXNlcnNcdmR1XGRvY3VtZW50c1x2aXN1YWwgc3R1ZGlvIDIwMTdcUHJvamVjdHNcV2ViQXBwbGljY
XRpb24xXFdlYkFwcGxpY2F0aW9uMVxhcGlcVGVlcGVl?=",
"server": "Kestrel",
"x-powered-by": "ASP.NET",
"content-length": "0",
"content-type": null
}
Here is my Startup.cs config
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
//services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder("IdentityApplication").RequireAuthenticatedUser().Build());
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new ApiKeyScheme() { In = "header", Description = "Please insert JWT with Bearer into field", Name = "Authorization", Type = "apiKey" });
});
services.AddMvc();
services.Configure<IISOptions>(options =>
{
options.AutomaticAuthentication = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
ConfigureAuth(app);
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
}
I'm using a token passed in header "Authorization" to every api request.
Every requets are working if I execute them in POSTMAN or from an application.
But with swagger it returns me the 401 error : Unauthorized.
I checked if the token is in the header of the request, it is.
Here is an exemple of the request header of a get request from swagger :
Accept:text/plain
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Authorization:Bearer MyToken

Angular2 with ASP.NET Core CORS issues when sending POST request

Having issues when sending a POST request via my Angular 2 service to an ASP.NET Core API. I am currently getting a HTTP 500 error:
"XMLHttpRequest cannot load http://localhost:51014/api/sites. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 500."
I am not getting this error on GET requests and as far as I can see I have CORS setup correctly server side?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddCors();
services.AddMvc();
....
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseCors(builder =>
builder.WithOrigins("http://localhost:3000")
.AllowAnyHeader()
.AllowAnyMethod());
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
SitesContoller.cs
// POST: api/Sites
[HttpPost]
public async Task<IActionResult> PostSite([FromBody] Site site)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Sites.Add(site);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (SiteExists(site.Id))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
throw;
}
}
return CreatedAtAction("GetSite", new { id = site.Id }, site);
}
My Angular 2 service:
site.service.ts snippet
public createSite(site: Site): Observable<Site> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
let body = JSON.stringify(site);
return this.http
.post(this.apiUrl + 'sites', { body }, options)
.map((res: Response) => res.json());
}
You need to add the EnableCors attribute to your SiteController class. Like this
[EnableCors(origins: "http://<SOME_SITE>", headers: "*", methods: "*")]
public class SiteController {
.....
}
refer to this link
Cannot tell from you code snippet this is the case, bit you do need it.

Resources