Route table in MVC6 for web API not working - asp.net

I have the following code in Startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "DefaultApi",
template: "api/{controller=Customers}/{id?}");
});
}
}
… and declared the MVC dependency in project.json:
{
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Server.IIS": "1.0.0-beta3",
"Microsoft.AspNet.Mvc": "6.0.0-beta3"
},
"frameworks": {
"aspnet50": { },
"aspnetcore50": { }
},
"bundleExclude": [
"node_modules",
"bower_components",
"**.kproj",
"**.user",
"**.vspscc"
],
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
]
}
I have the following controller:
using Microsoft.AspNet.Mvc;
using System.Collections.Generic;
namespace WebApplication1.Controllers
{
public class CustomersController : Controller
{
private List<Customer> _Customers = new List<Controllers.Customer>();
public CustomersController()
{
_Customers.Add(new Customer() { ID = 1, Name = "Fred" });
_Customers.Add(new Customer() { ID = 2, Name = "Bob" });
_Customers.Add(new Customer() { ID = 3, Name = "Tim" });
}
public List<Customer> Get()
{
return _Customers;
}
public Customer Get(int ID)
{
Customer Customer = _Customers.Find(c => c.ID == ID);
return Customer;
}
}
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
}
}
… but when I browse to /api/customers or /api/customers/1 I get a 404 Not Found
Am is missing something? Is the route table for web API in MVC6 supported yet?

If you are trying to port your Asp.Net Web API to MVC 6, you need to have Web API Compatibility Shim . The default startup class code explains it
// Uncomment the following line to add Web API servcies which makes it easier to port Web API 2 controllers.
// You need to add Microsoft.AspNet.Mvc.WebApiCompatShim package to project.json
// services.AddWebApiConventions();
By default it is switched off. Once enabled, you can configure web api route as
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
// Uncomment the following line to add a route for porting Web API 2 controllers.
// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
});
More details here

I am bit late to answer this, but you need to put routing attribute
[Route("api/[controller]")]
and remove the api from
template: "api/{controller=Customers}/{id?}");

Related

React-Admin with .net .The response to 'getList' must be like { data : [{ id: 123, ...}, ...] }, but the received data items do not have an 'id' key

I have an ASP.NET Core Web API and a React client. I'm trying to build admin dashboard with React-Admin. My problem is when I receive the data from server, my object are with property Id (uppercase), then in console I'm getting an error
The response to 'getList' must be like { data : [{ id: 123, ...}, ...] }, but the received data items do not have an 'id' key
I tried making new test class with property id (lowercase) in my server and then the problem is gone.
How can I fix this issue?
This is my test class and its working.
public class CityModel
{
public string id { get; set; }
public string Name { get; set; }
}
[HttpGet("Cities")]
public CityModel[] GetCities()
{
var city1 = new CityModel()
{
id = "ahsxge",
Name = "Berlin"
};
var city2 = new CityModel()
{
id = "axhdagw",
Name = "London"
};
var list = new List<CityModel>();
list.Add(city1);
list.Add(city2);
Response.Headers.Add("Access-Control-Expose-Headers", "X-Total-Count");
Response.Headers.Add("X-Total-Count", list.Count.ToString());
return list.ToArray();
}
This is my component in react :
const AppAdmin = () => {
const jwt = localStorage.getItem("jwt");
const httpClient = (url, options = {}) => {
options.user = {
authenticated: true,
token: 'Bearer ' + jwt
};
return fetchUtils.fetchJson(url, options);
};
const dataProvider = jsonServerProvider('https://localhost:44366/api', httpClient);
dataProvider.getList('Cities/Cities', {
pagination: { page: 1, perPage: 15 },
sort: { field: 'Name', order: 'ASC' },
})
.then(response => console.log(response));
return (
<Admin dataProvider={dataProvider}>
<Resource name='Cities/Cities' list={CitiesList} />
</Admin>
)
}
export default AppAdmin
You can configure the json converter to use camelCase serialization int the ConfigureServices method in the Startup.cs file the following way:
services
.AddControllers()
.AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
})
This way you can use PascalCase properties in your c# code (which you should do), but your client will recieve camelCase json properties.

How to set the minimum loglevel with serilog's application insights sink

I'm using Serilog to log to application insights, but I am only getting the log levels warning, error, and critical to be logged. In appsettings.json, I have the minimum loglevel set to verbose, and this logs everything with the file and console sinks.
By default, application insights can only logs Warning and above. In code (in configure logging), I can add a filter to override this default of warning and above. I preferer to do this in appsettings, with the other logging configuration.
How so I use appsettings to allow Serilog to log all levels to Application Insights?
I am getting some logs in application insights so the connectivity is
working.
I see all loglevels in the logfile and on the console.
Where configuting logging,if I add LogLevel filer (commented out) I can make it work. I needs to work from appsettings.
host.ConfigureLogging(
loggingBuilder =>
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
loggingBuilder.AddApplicationInsights();
loggingBuilder.AddSerilog(logger, dispose: true);
//loggingBuilder.AddFilter<ApplicationInsightsLoggerProvider>("", LogLevel.Trace);
}
);
This is my code and the results:
Here is my appsettings:
"Serilog": {
"AllowedHosts": "*",
"Enrich": [ "WithMachineName" ],
"MinimumLevel": {
"Default": "Verbose",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"fileSizeLimitBytes": "1048576",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact , Version=1.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10",
"path": "c:\\LogFiles\\Publisher\\Test1\\_log.txt",
"retainedFileCountLimit": null,
"rollingInterval": "Day",
"rollOnFileSizeLimit": "true"
}
},
{
"Name": "ApplicationInsights",
"Args": {
"restrictedToMinimumLevel": "Verbose",
"telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
}
}
]
},
Here's my testing result:
I created my asp.net core mvc application and here's my appsettings.json:
{
"AllowedHosts": "*",
"Serilog": {
"Using": [],
"MinimumLevel": "Verbose",
"WriteTo": [
{
"Name": "Console",
"Args": {
"restrictedToMinimumLevel": "Verbose",
"outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message} {NewLine}{Exception}"
}
},
{
"Name": "ApplicationInsights",
"Args": {
"instrumentationKey": "instrument_key_here",
"restrictedToMinimumLevel": "Verbose",
"telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId"],
"Properties": {
"Application": "Sample"
}
}
}
My program.cs, add read configuration code and add UseSerilog():
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
namespace serilog_appsetting_file_appinsights
{
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).UseSerilog();
}
}
My packages:
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.17.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.ApplicationInsights" Version="3.1.0" />
</ItemGroup>
My controller file:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Serilog;
using serilog_appsetting_file_appinsights.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace serilog_appsetting_file_appinsights.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
Log.Verbose("serilog_verbose_info");
Log.Debug("serilog_debug_info");
Log.Information("serilog_information_info");
Log.Warning("serilog_warning_info");
Log.Error("serilog_error_info");
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
When debuging in localhost, we can see app insights capturing logs like this, and see console log

Spatial Point data type is not serialized correctly when using custom System.Text.Json serializer for Cosmos .NET v3 SDK

I want to use the System.Text.Json Json serializer with the Cosmos .NET v3 SDK.
So I use:
var client = new CosmosClientBuilder
(accountEndpoint: "https://localhost:8081", "<key>")
.WithCustomSerializer(new SystemTextJsonCosmosSerializer(new JsonSerializerOptions {}))
.Build();
With the custom serializer being (taken from Github issue):
public class SystemTextJsonCosmosSerializer : CosmosSerializer
{
private readonly JsonSerializerOptions _options;
public SystemTextJsonCosmosSerializer(JsonSerializerOptions options)
{
_options = options;
}
/// <inheritdoc />
public override T FromStream<T>(Stream stream)
{
// Have to dispose of the stream, otherwise the Cosmos SDK throws.
// https://github.com/Azure/azure-cosmos-dotnet-v3/blob/0843cae3c252dd49aa8e392623d7eaaed7eb712b/Microsoft.Azure.Cosmos/src/Serializer/CosmosJsonSerializerWrapper.cs#L22
// https://github.com/Azure/azure-cosmos-dotnet-v3/blob/0843cae3c252dd49aa8e392623d7eaaed7eb712b/Microsoft.Azure.Cosmos/src/Serializer/CosmosJsonDotNetSerializer.cs#L73
using (stream)
{
// TODO Would be more efficient if CosmosSerializer supported async
using var memory = new MemoryStream((int)stream.Length);
stream.CopyTo(memory);
byte[] utf8Json = memory.ToArray();
return JsonSerializer.Deserialize<T>(utf8Json, _options);
}
}
/// <inheritdoc />
public override Stream ToStream<T>(T input)
{
byte[] utf8Json = JsonSerializer.SerializeToUtf8Bytes(input, _options);
return new MemoryStream(utf8Json);
}
}
However, the Point data type is then not properly serilized.
public Point Location { get; set; }
It should be (as with Cosmos .NET SDK v3/Newtonsoft , v4 Preview/System.Text.Json) be:
"location": {
"type": "Point",
"coordinates": [
8.0000,
47.0000
]
},
But instead it ends up being:
"location": {
"Position": {
"Coordinates": [
8.0000,
47.0000
],
"Longitude": 8.0000,
"Latitude": 47.0000,
"Altitude": null
},
"Crs": {
"Type": 0
},
"Type": 0,
"BoundingBox": null,
"AdditionalProperties": {}
},
Anyone has idea on why, and how I can make it serialize properly?

How to use Entity Framework 7 in class library?

I want to make the work with data in a class library with asp.net core 1. I created MyDbContext in a class library:
public class MyDbContext: DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserProfile> Profiles { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// maybe need to add foreign key
modelBuilder.Entity<User>()
.HasOne(p => p.Profile)
.WithOne(u => u.User)
.HasForeignKey<UserProfile>(p => p.UserId);
}
}
My project.json in class library:
{
"version": "1.0.0-*",
"description": "DatabaseCore Class Library",
"authors": [ "alex-pc" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"frameworks": {
"net451": {
"dependencies": {
"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final",
"EntityFramework.Commands": "7.0.0-rc1-final"
},
"frameworkAssemblies": {
"System.Runtime": "4.0.10.0",
"System.Data.Entity": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.ComponentModel.DataAnnotations": "4.0.0.0"
}
}
},
"dependencies": {
}
}
And updated startup.cs in web application:
public class Startup
{
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
if (env.IsDevelopment())
{
builder.AddApplicationInsightsSettings(developerMode: true);
}
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options =>
{
options.UseSqlServer(Configuration["Data:ConnectionString"]);
});
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
//services.AddAuthorization(options =>
//{
// options.AddPolicy("API", policy =>
// {
// policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
// policy.RequireAuthenticatedUser();
// });
//});
services.AddAuthentication();
services.AddCaching();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseIISPlatformHandler();
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.Audience = "resource_server_1";
options.Authority = "http://localhost:4871/";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters.ValidateLifetime = true;
});
// Add a new middleware issuing tokens.
app.UseOpenIdConnectServer(options =>
{
options.AllowInsecureHttp = true;
options.AuthorizationEndpointPath = PathString.Empty;
options.TokenEndpointPath = "/connect/token";
options.Provider = new AuthorizationProvider();
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
My appsettings.json:
{
"ApplicationInsights": {
"InstrumentationKey": ""
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Verbose",
"System": "Information",
"Microsoft": "Information"
}
},
"Data": {
"ConnectionString": "Data Source=DESKTOP-R3AP4AT\\SQLEXPRESS;Initial Catalog=mydb;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
}
}
Now I want to make the migration to create the database. Using commands in cmd
dnvm use 1.0.0-rc1-final
dnx ef migrations add MyFirstMigration
dnx ef database update
First, all performed well, the database was created, but does not create a table, database is empty. If i added this code:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
#"Data Source=DESKTOP-R3AP4AT\\SQLEXPRESS;Initial Catalog=mydb;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False");
base.OnConfiguring(optionsBuilder);
}
error - instance crashes,
Result:
I think you missed
"commands": {
"ef": "EntityFramework.Commands"
},
in the project.json of your library.
When I moved my EF model into a library I followed the instructions from here: http://www.jerriepelser.com/blog/moving-entity-framework-7-models-to-external-project
Meanwhile I moved from RC1 to RC2 nightly and my project.json of the library looks like this.
{
"version": "1.0.0-*",
"description": "DhrData Class Library",
"authors": [ "noox" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"dependencies": {
"Microsoft.EntityFrameworkCore.Commands": "1.0.0-rc2-20270",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0-rc2-20270"
},
"commands": {
"ef": "EntityFramework.Commands"
},
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.Reflection": ""
}
},
"dnxcore50": {
"dependencies": { }
}
}
}
Lately I did not use migration because I had too much changes while playing around with EF Core. Instead I created a console application which recreates the database and fills it with some data. This is already for RC2 (and you probably do not need the custom Appsettings).
using System;
using DhrData.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore;
namespace DhrConsoleApp
{
public class Program
{
private IServiceProvider serviceProvider;
public IConfiguration Configuration { get; set; }
public AppSettings AppSettings { get; set; } // object for custom configuration
private void ConfigureServices(IServiceCollection serviceCollection)
{
// Add framework services.
serviceCollection
//.AddEntityFramework() // RC1
.AddEntityFrameworkSqlServer() // RC2
.AddDbContext<DhrDbContext>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
serviceCollection
.AddScoped(p => new DhrDbContext(p.GetService<DbContextOptions<DhrDbContext>>()));
serviceCollection.Configure<AppSettings>(s => Configuration.GetSection("AppSettings"));
}
public static void Main(string[] args)
{
var applicationEnvironment = PlatformServices.Default.Application;
new Program(applicationEnvironment);
}
public Program(IApplicationEnvironment env)
{
//var loggerFactory = new LoggerFactory();
//loggerFactory.AddConsole(LogLevel.Debug);
// Set up configuration sources.
var configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile("config.{env.EnvironmentName.ToLower()}.json", optional: true)
.AddEnvironmentVariables();
Configuration = configurationBuilder.Build();
// custom application settings
AppSettings = new AppSettings();
Configuration.GetSection("AppSettings").Bind(AppSettings);
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
serviceProvider = serviceCollection.BuildServiceProvider();
using (var dbContext = serviceProvider.GetService<DhrDbContext>())
{
RecreateDb(dbContext);
}
}
private void RecreateDb(DhrDbContext dbContext)
{
Console.WriteLine("EnsureDeleted ...");
dbContext.Database.EnsureDeleted();
Console.WriteLine("EnsureDeleted ... Done!");
Console.WriteLine("EnsureCreated ...");
dbContext.Database.EnsureCreated();
Console.WriteLine("EnsureCreated ... Done!");
Console.WriteLine("");
}
}
}
If not necessary I would not recommend to update to RC2 as long as it is not final. It's a pain. But there are quite some bugs in EF RC1 which have already been fixed in RC2 nightly builds.
The recommended approach is to leave the connection string in the AddDbContext<TContext>() call in the application's Startup.cs and to use MigrationsAssembly() to tell EF where the migrations will be located (which should probably be alongside the DbContext). E.g.:
services.AddEntityFramework().AddDbContext<MyDbContext>(options => options
.UseSqlServer(connectionString)
.MigrationsAssembly(assemblyName));

Calling a specific method in my Web API controller

So I'm trying to make a call to this specific method:
[HttpGet]
public int GetLogin(string u, string p)
{
UserLogin uL = new UserLogin();
return (int)uL.Authenticate(u, p);
}
However it keeps calling this method in my Controller instead:
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
^^ Which is the generated code in the Controller.
Here Angular code for my factory and, more importantly, my URI:
var loginFactory = angular.module('loginService', ['ngResource'])
loginFactory.factory('UserLogin', function ($resource) {
return $resource('api/login?:username?:password', {}, {
query: {
method: 'GET',
params: { username: 'hunter', password: 'hunter' },
isArray:true
}
});
});
Any insight would be much appreciated. Thanks.
Change your resource to this:
$resource('api/GetLogin?u=:username&p=:password', {}, { // if that's the right route
query: {
method: 'GET',
params: { username: 'hunter', password: 'hunter' },
isArray:true
}
});
Is your request going like api/login?username=something&password=something...?
if yes, the parameter names on the action should match the query string parameter names:
public int GetLogin(string **username**, string **password**)

Resources