Decompressing Web Api response - winforms

I am compressing web Api responses with following config
<system.webServer>
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="application/*" enabled="true" />
<add mimeType="*/*" enabled="false" />
</dynamicTypes>
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="application/*" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
</httpCompression>
<urlCompression doStaticCompression="true" doDynamicCompression="true" />
Now when i consume this in a Win Form applications and try to do the follwoing
var rawData = await response.Content.ReadAsStringAsync();
var deserializedData = JsonConvert.DeserializeObject<Employees[]>(rawData).ToList();
it fails on
var deserializedData = JsonConvert.DeserializeObject(rawData).ToList();
the Error message is
{"Unexpected character encountered while parsing value: . Path '', line 0, position 0."}
I guess this is due to the fact that content is gziped and not being deserialized. Can anyone suggest a solution ? This works locally fine as local IIS is not gzip enabled

You need to enable automatic GZip decompression:
var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip };
var client = new HttpClient(handler);

Related

Adding Web API as Application to Website in IIS, where does Frontend point to?

I have two separate processes running for my website. The frontend is reactjs and the backend is asp.net core 6.
I have a reactjs website running on localhost:9000 on IIS on a server.
I've added the .net web api published files to the above website as an application with it's own application pool.
In the .env REACT_APP_API_URL: I don't know how to point to the API, before i just used localhost:5000, how do I change this to use the application url?
Tried: */api/controllername, localhost/api/controllername, IPADDRESS/api/controllername,
COMPUTERNAME/api/controllername etc.
Here is the Program.cs
using api;
using api.Services;
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseIIS();
builder.Services.Configure<IISServerOptions>(options =>
{
options.AutomaticAuthentication = false;
});
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowOrigin",
options =>
{
options.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "Project.Cookies";
options.ExpireTimeSpan = TimeSpan.FromHours(12);
options.SlidingExpiration = true;
options.LoginPath = "/User/Login";
options.LogoutPath = "/User/Logout";
options.Cookie.IsEssential = true;
});
builder.Services.AddDbContext<DbDataContext>();
builder.Services.AddScoped<IProjectService, ProjectService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment() || app.Environment.IsProduction())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Here is the web.config for the backend:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\api.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
<!--ProjectGuid: -->
And here is the web.config for the reactjs frontend:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="React Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
I'm in over my head on how to get these talking to each other.
I can run the dotnet from the commandline and have reactjs talk to that localhost on the server (this wont work when connecting to the website from outside the server). But I need these working in IIS.
Any help would be greatly appreciated.
The front end and the backend application should use same port. In your react project, you should use something like const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href'); or process.env.PORT. And your frontend will start with port which you create the main website. You can find it like below.
The main application and virtual application host in same site with different application. And they also should use same port. You can check my test result.
And you can open the frontend application by clicking the Browse *:8031 in the right panel in my picture.
You can access your api application via https://localhost:8031/api/{apiname or controller/action}.
In your case, it should be http://localhost:port/{hidden by red pen}api/controllername.

How to deploy a NextJs SSR React app on Azure

I have been trying to deploy a Server-side rendered react app I built with NextJS on Azure. I set up the Azure pipeline and release successfully but after running it the app doesn't seem to load up when I went to the azure website URL. The build file content is different from a client rendered app. Please share a resource or explanation about deploying SSR React apps (on Azure).
I used this resource to set up the pipeline and I encountered no error but the URL is still not loading the app.
As mentioned by #James in the comment in Doris's answer, using a custom server.js in a Next.js app would make it slower in production because the routes aren't pre-built. Using next build followed by next start would solve this issue. But for you to be able to do that, you should not have a server.js.
And as per Microsoft documentation for deploying Node JS / Next JS application on Azure Linux web app, the recommended way is to use PM2 rather than using npm start (or) node server.js.
So you don't need server.js or web.config. All you need to do is to have a file called ecosystem.config.js with the below content
module.exports = {
apps: [
{
name: "my-nextJs-site",
script: "./node_modules/next/dist/bin/next",
args: "start -p " + (process.env.PORT || 3000),
watch: false,
autorestart: true,
},
],
};
and have the startup command for your Azure app to be this
pm2 --no-daemon start /home/site/wwwroot/ecosystem.config.js
and no change in your package.json scripts
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
Azure Windows App Service
pm2 is not available in Azure Windows Service - it uses IIS Server. Check out the following answer and its linked questions.
Other useful resources:
Deploying a Node.js application on Windows IIS using a reverse proxy
Deploying Nextjs on IIS server without custom server
Deploying Nextjs on Digital Ocean - App Platform
You need two file: server.js and web.config, and modify package.json like below. I've answered a question about deploy nextjs app step by step, you could have a look at this.
package.json modify.
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "node server.js"
server.js (create this file with the code below:)
const { createServer } = require('http')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = new URL(req.url, 'http://w.w')
const { pathname, query } = parsedUrl
if (pathname === '/a') {
app.render(req, res, '/a', query)
} else if (pathname === '/b') {
app.render(req, res, '/b', query)
} else {
handle(req, res, parsedUrl)
}
}).listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
web.config (create this file with the code below:)
<?xml version="1.0" encoding="utf-8"?>
<!--
This configuration file is required if iisnode is used to run node processes behind
IIS or IIS Express. For more information, visit:
https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->
<configuration>
<system.webServer>
<!-- Visit http://blogs.msdn.com/b/windowsazure/archive/2013/11/14/introduction-to-websockets-on-windows-azure-web-sites.aspx for more information on WebSocket support -->
<webSocket enabled="false" />
<handlers>
<!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<!-- Do not interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js\/debug[\/]?" />
</rule>
<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
<!-- All other URLs are mapped to the node.js site entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server.js"/>
</rule>
</rules>
</rewrite>
<!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<!-- Make sure error responses are left untouched -->
<httpErrors existingResponse="PassThrough" />
<!--
You can control how Node is hosted within IIS using the following options:
* watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server
* node_env: will be propagated to node as NODE_ENV environment variable
* debuggingEnabled - controls whether the built-in debugger is enabled
See https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config for a full list of options
-->
<!--<iisnode watchedFiles="web.config;*.js"/>-->
</system.webServer>
</configuration>

What's wrong with my connection string (can not access it through ConfiguratioManager)?

I'm trying to connect to Azure SQL, but I get error The underlying provider failed on Open
I discovered that I have put connection string in the wrong app.config - moved it to the executable project's app.config, but still the same result
When I check ConfigurationManager.ConnectionStrings["AzureDatabase"] it returns null. And when I try to connect it just uses default SQLExpress.
When I check build folder of my executable application - there is an <app_name>.exe.config file with "AzureDatabase". I'm stuck on where to search from here
This is my DbContext class
[DbConfigurationType(typeof(AzureDbConfiguration))]
public class ProductDbContext : DbContext
{
//Db sets
static MyContext()
{
//I don't want to change database from code
Database.SetInitializer<MyContext>(null);
}
public MyContext() : base("AzureDatabase") //this is my connectionstring
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//some mappings
}
}
This is AzureDbConfiguration
public class AzureDbConfiguration : DbConfiguration
{
public AzureDbConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy(2, TimeSpan.FromSeconds(10)));
}
}
This is my app.config file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="AzureDatabase"
connectionString="Server=tcp:xxx.database.windows.net,1433;Initial Catalog=xxx;Persist Security Info=False;User ID=xxxx;Password=xxxxx;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
Any ideas on what's wrong here?
It turns out the problem was that I was trying to work with app.config from Class library (I was trying to create an integration tests)
One of potential solutions is to create a separate settings file for Class library. You can read about it in this StackOverflow post

Cassette.Nancy unbundled files returning 404

I've added Cassette.Nancy to an existing Nancy web project. This works fine when I set CassetteNancyStartup.OptimizeOutput = true; but when this is set to false I get 404 on the unbundled resources.
Here's my set up.
I'm using the following packages:
Cassette.Nancy version="2.1.1"
Cassette version="2.4.1"
Nancy version="0.22.2"
Nancy.Owin version="0.22.2"
Nancy.Viewengines.Razor version="0.22.2"
The files are like so:
Content
file1.css
file2.css
Scripts
script1.js
script2.js
CassetteBundleConfiguration:
public class CassetteBundleConfiguration : IConfiguration<BundleCollection>
{
public void Configure(BundleCollection bundles)
{
bundles.AddPerSubDirectory<StylesheetBundle>("Content");
bundles.Add<ScriptBundle>("Scripts");
}
}
in my _Layout.cshtml:
#{
Bundles.Reference("Content");
Bundles.Reference("Scripts");
}
#Bundles.RenderStylesheets()
#Bundles.RenderScripts()
And finally in Bootstrapper:
public Bootstrapper()
{
CassetteNancyStartup.OptimizeOutput = false;
}
Like I say this works fine when CassetteNancyStartup.OptimizeOutput is set to true but when false each of the resources return a 404 like this one:
GET http://localhost:10005/_cassette/asset/Content/file1.css?cf7a7edf515a8184a0c53ec498c583cc64bb0e63 404 (Not Found)
Any suggestions?
This issue was down to me not adding the Owin handler in the web.config. Adding this fixed it.
<system.webServer>
<handlers>
<add name="Owin" verb="*" path="*" type="Microsoft.Owin.Host.SystemWeb.OwinHttpHandler, Microsoft.Owin.Host.SystemWeb" />
</handlers>
</system.webServer>

WCF + Silverlight + HttpContext.Current.Session is null

my problem....
i am tryingo to access session from Silverlight and WCF basicHttpBinding...
i saw some posts where it's possible (http://www.dotnetspider.com/Silverlight-Tutorial-317.aspx)
Mys cenario is:
Silvelright 4 FW 3.5
in web.config i have
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ViewModelDemo.Web.Service1Behavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="ViewModelDemo.Web.Service1Behavior" name="ViewModelDemo.Web.Service1">
<endpoint address="" binding="basicHttpBinding" contract="ViewModelDemo.Web.Service1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
and my service:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Service1
{
[OperationContract]
publicvoid Test()
{
var session = System.Web.HttpContext.Current.Session;
}
}
and it''s call
var client = new Service1Client();
client.GetUserMacroFunctionsCompleted += new System.EventHandler<GetUserMacroFunctionsCompletedEventArgs>(client_GetUserMacroFunctionsCompleted);
client.GetUserMacroFunctionsAsync();
void client_GetUserMacroFunctionsCompleted(object sender, GetUserMacroFunctionsCompletedEventArgs e)
{
var test = ((Collection<Function>)e.Result);
}
HttpContext.Current is always null!
Any suggestions?
Yes HttpContext must be always null because your service configuration doesn't setup ASP.NET compatibility and your service doesn't require ASP.NET compatibility.
Add this to your configuration:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
And change AspNetCompatibilityRequirements so that your service cannot be hosted without former configuration:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
This link probably to help you.
http://blogs.msdn.com/b/sajay/archive/2006/08/03/687361.aspx
aspNetCompatibilityEnabled="true" doesn't help me till I set allowCookies="true" on the client binding configuration.
Update your web.config file to include
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
This should work, or else also change the AspNetCompatibilityRequirementsMode attribute on the contract to Required.

Resources