I am trying to setup a unit test in F# with asp.net core. with the code below. but the code to the server fails with a 404 Not found.
Is there a reason why the controller is being discovered by the framework ?
module Tests
open System
open Xunit
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.DependencyInjection
type Startup () =
member __.ConfigureServices(services: IServiceCollection) =
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2) |> ignore
member __.Configure(app: IApplicationBuilder, env: IHostingEnvironment) =
app.UseMvc() |> ignore
[<Route("api")>]
[<ApiController>]
type TestController() =
inherit ControllerBase()
[<HttpGet("testGet")>]
member __.TestGet() =
121221
[<HttpPost("testPost")>]
member __.TestPost() =
String.Empty
let buildHost(baseAdress:string) =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.UseUrls(baseAdress)
.Build()
[<Fact>]
let ``My test`` () =
let baseAdress = "https://localhost:9000"
use host = buildHost(baseAdress)
host.RunAsync() |> ignore
let client = new System.Net.Http.HttpClient(BaseAddress = new System.Uri(baseAdress))
let result = client.GetAsync("api/testGet").Result
Assert.Equal(System.Net.HttpStatusCode.OK, result.StatusCode)
Assert.True(result.IsSuccessStatusCode, result.Content.ReadAsStringAsync().Result )
Here is the content of the fsproj File
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="Tests.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Microsoft.NETCore.App" Version="2.2.2" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
</Project>
Except the reasons commented above by #Kirk Larkin, you need put your controller into a namespace instead of module directly.
namespace App //<------------- put your controller into a namespace
// ... open
type Startup () =
...
[<Route("api")>]
[<ApiController>]
type TestController() =
...
module Tests=
// ... open
let buildHost(baseAdress:string) =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.UseUrls(baseAdress)
.Build()
[<Fact>]
let ``My test`` () =
...
Also note you're starting the server by an asynchronous method host.RunAsync() |> ignore. Although the above test code works fine for me, there's a chance your HttpClient will send requests before the server is ready. A good way is to add a reference to the Microsoft.AspNetCore.TestHost package and using the built-in TestHost instead:
let hostBuilder =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
use server = new Microsoft.AspNetCore.TestHost.TestServer(hostBuilder)
use client = server.CreateClient()
let result = client.GetAsync("api/testGet").Result // a quick & dirty test
Assert.Equal(System.Net.HttpStatusCode.OK, result.StatusCode)
Assert.True(result.IsSuccessStatusCode, result.Content.ReadAsStringAsync().Result )
[Edit]:
I'm now sure it is a behavior by design. See source code on GitHub:
// We only consider public top-level classes as controllers. IsPublic returns false for nested
// classes, regardless of visibility modifiers
if (!typeInfo.IsPublic)
{
return false;
}
Since the class within a F# module will be compiled into a nested class, it won't be considered as a Controller at all.
On top of the answer that #itminus provided. the issue can be resolved by configuring the application manager in configureServices like so
member this.ConfigureServices(services: IServiceCollection) =
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApplicationPartManager(fun m ->
m.FeatureProviders.Add
{
new Microsoft.AspNetCore.Mvc.Controllers.ControllerFeatureProvider() with
member __.IsController (typeInfo:System.Reflection.TypeInfo) =
(typeof<ControllerBase>.IsAssignableFrom(typeInfo)) || base. IsController(typeInfo)
}
) |> ignore
services.AddRouteAnalyzer() |> ignore
Related
<PackageReference Include="OpenTelemetry" Version="1.2.0-rc3" />
<PackageReference Include="OpenTelemetry.Api" Version="1.2.0-rc3" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.2.0-rc3" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9" />
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System.Diagnostics;
var builder = WebApplication.CreateBuilder(args);
var serviceName = "Demo";
var serviceVersion = "1.0.0";
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddOpenTelemetryTracing(b =>
{
b
.SetSampler(new AlwaysOnSampler())
.AddSource(serviceName)
.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService(serviceName: serviceName, serviceVersion: serviceVersion))
.AddConsoleExporter()
;
});
builder.Services.AddSingleton(TracerProvider.Default.GetTracer(serviceName));
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapGet("/hello", (Tracer tracer) =>
{
var current = Activity.Current;
using var span = tracer.StartActiveSpan("hello-span");
});
app.Run();
“hello-span” is output. I debug the variable current, and there is an operation name "Microsoft. Aspnetcore. Hosting. Httprequestin".
However, this activity is not output to the console. I want to output it. What should I do.
I've found a solution
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9" />
This package creates a new activity based on the built-in requestin activity and provides a way to enrich this activity.
So I can add some tags and filters in the request process.
Filter the data according to the active tag, and then export it.
I have test the code you provided, and it works for me. Please check the result first, does it you want?
My test steps:
Choose IIS Express first, then run it.
Open output window, and choose Project_Name-Asp.Net Core Web Server.
Then you can check the message here when access the url http://xxx:port/hello.
So I have this code to send email. The code tries to find the .cshtml for the email template.
This is my folder hierarchy:
Project
-- Email
---- EmailConfirmation.cshtml
---- ResetPassword.cshtml
This is the code to find the template:
public static string GetEmailTemplate(string templateName)
{
string path = Path.Combine(Config.Env.ContentRootPath, "Email", templateName);
string content = "";
// This text is added only once to the file.
if (File.Exists(path))
{
// Create a file to write to.
content = File.ReadAllText(path);
}
else
{
throw new Exception("The email template could not be found");
}
return content;
}
In Debug, this runs perfectly, but in production, the code cannot find the template.
How can I include the template in publish package?
I tried this in my .csproj:
<ItemGroup>
<Folder Include="ClientApp\" />
<Folder Include="Migrations\" />
<Views Include="Email\**" />
</ItemGroup>
It is not working.
Edit
I tried to set this:
But still not working.
So my problem was the Email folder was not copied to the published package. Add this code to .csproj:
<ItemGroup>
<None Include="Email\EmailConfirmation.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Email\ResetPassword.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Or you can set it in the project explorer like this:
For some reason, the build action has to be set to None, because .NetCore will treat Content differently and the folder will not be copied to the publishing package (maybe the content will be merge into .dll, I do not know).
Could you try this ?
string workingDirectory = Environment.CurrentDirectory;
string projectDirectory=Directory.GetParent(workingDirectory).Parent.Parent.FullName;
It will give you the Path to the project directory. Then you complete with the subdirectory which leads to your file.
RaceConditionTest.fs
namespace FConsole
module RaceConditionTest =
let test x =
...
Program.fs
open System
open FConsole
[<EntryPoint>]
let main argv =
RaceConditionTest.test 1000
0 // return an integer exit code
Then I run my console app (linux)
$ dotnet run
error FS0039: The namespace or module 'FConsole' is not defined.
There is only one test method in RaceConditionTest.fs
Is the order of files the problem? if so, How do I indicate the order of *.fs files?
as #boran suggested in his comments there is this FConsoleProject.fsproj
I just added my file before Program.fs
<ItemGroup>
<Compile Include="RaceConditionTest.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
We are trying to build an xproj project and an error about not being able to find the Microsoft.DotNet.Props file because it seems like its looking at the wrong directory.
Taking a look at the xml MSBuildExtensionsPath32 references C:\Program Files\dotnet\sdk\1.1.4\ where the directory Microsoft\VisualStudio\.. does not exist ... but the normal MSBuild directory C:\Program Files (x86)\MSBuild does have the directory for Microsoft.DotNet.Props file C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\DotNet\Microsoft.DotNet.Props
Here is the part of the XML
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
The error I see while building is:
error MSB4019: The imported project "C:\Program Files\dotnet\sdk\1.1.4\Microsoft\VisualStudio\v14.0\DotNet\Microsoft.DotNet.Props" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
If anyone has any idea what is happening, help would be great
EDIT:
The build is invoked from a Jenkins project on Windows Server 2012 R2.
The VM image is from Azure market place "MicrosoftVisualStudio / VisualStudio / VS-2015-Comm-VSU3-AzureSDK-29-WS2012R2 / 2017.10.12" - which comes with Visual Studio 2015 community edition with update 3.
Azure SDK 2.9. Upgraded Node from old v0.12 to v8.x. Upgraded .NET core from not sure what was installed to 1.1.4.
The xproj itself has no code - except small amount in Startup.cs to serve static files (code at bottom of post).
The application is also used in a Service Fabric project. The error does not come from building the .sln but when packaging up the .sfproj (it might be its not set to build in the sln but packaging will need to build it).
Startup.cs:
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Website
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
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();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 404
&& !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/index.html";
await next();
}
});
app.UseStaticFiles();
}
}
}
Edit: here is the whole xproj xml
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<ProjectGuid>17107df8-0cfa-6946-917a-a9b8765cf9ea</ProjectGuid>
<RootNamespace>Website</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<IsServiceFabricServiceProject>True</IsServiceFabricServiceProject>
</PropertyGroup>
<ItemGroup>
<DnxInvisibleContent Include="bower.json" />
<DnxInvisibleContent Include=".bowerrc" />
</ItemGroup>
<ItemGroup>
<DnxInvisibleFolder Include="wwwroot\Angular\dist\" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b69-4b1e-b82e-3ada8210c987}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
You are trying to use a preview tooling (xproj) with the 1.1.4 version of the .NET Core Sdk. The preview tooling available in VS 2015 does not work with the 1.0+ stable tooling for .NET Core.
Make sure that a preview2 version of the .NET Core SDK is installed both on your development machines and Jenkins server - e.g. 1.0.0-preview2-003156 - and that a global.json file exists your solution directory to tell VS to use this preview version of the SDK:
{
"sdk": {
"version": "1.0.0-preview2-003156"
}
}
As a long-term solution I recommend moving to the stable and supported .NET Core tooling by migrating to VS 2017.
My code is as follows:
...
propertyGroup1.AddProperty("DeployOnBuild", "true");
propertyGroup1.AddProperty("DeployTarget", "MSDeployPublish");
propertyGroup1.AddProperty("MSDeployServiceUrl", "localhost");
propertyGroup1.AddProperty("DeployIisAppPath", "local.projects.com");
propertyGroup1.AddProperty("MSDeployPublishMethod", "InProc");
propertyGroup1.AddProperty("AllowUntrustedCertificate", "true");
...
cSharpProject.ProjectCollection.RegisterLogger(cSharpLogger);
try {
buildResult = cSharpProject.Build();
...
buildResult is set to false. However, it does not raise any error to the logger. And the project is not deployed.
I'm running my exe in Administrator mode.
Any help is apprecited.
Thanks
Chris
hint: CSAutoParameterize.parameters and other subfolders have not been created.
<Target Name="WebPublish">
<MsBuild Projects="MyProject.csproj" Properties="DeployOnBuild=true;DeployTarget=MSDeployPublish;MSDeployServiceUrl=localhost;DeployIisAppPath=local.website.com;MSDeployPublishMethod=RemoteAgent;AllowUntrustedCertificate=true;Username=someuser;password=somepassword" />
</Target>
I was able to add this target to the project and call just that specific target.