Blazor Custom element not working with Blazor Server and Angular - websocket

I created an app using Angular with ASP.Net core project template to work with Blazor Custom Elements in Blazor Server. I created the component and registered it as a custom element as suggested in the official documentation. I added the proxy config and other changes for angular by referring to ring the asp labs sample.
When I ran the sample I faced the below issue and the custom element is not working as expected.
[webpack-dev-server] Live Reloading enabled.
blazor.server.js:1 WebSocket connection to 'wss://localhost:44476/_blazor?id=gxW6SsA09YwNC2qyxoqguw' failed:
(anonymous) # blazor.server.js:1
blazor.server.js:1 [2023-01-13T18:51:43.964Z] Information: (WebSockets transport) There was an error with the transport.
blazor.server.js:1 [2023-01-13T18:51:43.967Z] Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.
Here is my sample repo
It seems I am missing something, how can I resolve the error and get the custom element working?
As Astrid suggested in the comments, I am attaching the details of all the codes I have added to the angular with a core project template.
Added Custom elements NuGet in AngularWithCore.csProj
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.CustomElements" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="7.0.1" />
</ItemGroup>
Created Counter.razor and added under AngularWithCore/Pages/Counter.razor
<h1>#Title</h1>
<p role="status">Current count: #currentCount</p>
<p>Increment amount: #IncrementAmount</p>
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
#code {
private int currentCount = 0;
private DateTime date = DateTime.Today;
[Parameter] public string Title { get; set; } = "Blazor Counter";
[Parameter] public int? IncrementAmount { get; set; }
private void IncrementCount()
{
currentCount += IncrementAmount.GetValueOrDefault(1);
}
}
Added Blazorhub, razor pages, server-side blazor in the Program.cs and registered Counter as custom element
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(options =>
{
options.RootComponents.RegisterCustomElement<Counter>("my-blazor-counter");
});
var app = builder.Build();
app.MapBlazorHub();
Updated the Proxy config in client app
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:6937';
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
"/_content",
"/_framework",
"/_blazor"
],
target: target,
secure: false,
ws: true,
logLevel: "debug",
headers: {
Connection: 'Keep-Alive'
}
}
]
module.exports = PROXY_CONFIG;
Added Blazor script Index.HTML in the client app
<head>
<meta charset="utf-8" />
<title>AngularWithCore</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<script src="_content/Microsoft.AspNetCore.Components.CustomElements/Microsoft.AspNetCore.Components.CustomElements.lib.module.js"></script>
<script src="_framework/blazor.server.js"></script>
</head>
Added Blazor custom element in the angular page
<h1>Blazor Counter </h1>
<my-blazor-counter increment-amount=4></my-blazor-counter>

Removing the keep-alive header from the proxy did the trick and resolved the issue.
Thanks for the solution provided by the dotnet team in issue - 46091 .
I have updated my repo with the solution.
Here is the updated proxy config which resolved the issue.
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
"/_content",
"/_framework",
"/_blazor",
],
proxyTimeout: 3000,
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
},
{
context: [
"/_blazor"
],
target: target,
secure: false,
ws: true,
logLevel: "debug"
}
];

Related

Google old api of profile information is not working, What is the new API URL?

Google old profile API not working now.
I used this API link before,
(http://picasaweb.google.com/data/entry/api/user/sakiremail#gmail.com?alt=json)
but it's not working now. Can't get profile information. What is the new API URL?
here I have created a demo app to work with. Before test please create an auth client id. Also, add an authorized redirect URI to http://localhost if you are checking on local.
<html lang="en">
<head>
<meta name="google-signin-scope" content="profile email">
</head>
<body>
Login
<script>
/*
* Create form to request access token from Google's OAuth 2.0 server.
*/
function oauthSignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create <form> element to submit parameters to OAuth 2.0 endpoint.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
// Parameters to pass to OAuth 2.0 endpoint.
var params = {'client_id': 'YOUR_APP_CLIENT_ID',
'redirect_uri': 'AUTHENTICATED_REDIRECT_URI',
'response_type': 'token',
'scope': 'profile',
'include_granted_scopes': 'true',
'state': 'pass-through value'};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}
var hash = window.location.hash.substr(1);
var hashresult = hash.split('&').reduce(function (result, item) {
var parts = item.split('=');
result[parts[0]] = parts[1];
return result;
}, {});
if(hashresult.access_token){
console.log(hashresult.access_token);
fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token='+hashresult.access_token)
.then(function(response) {
return response.json();
})
.then(function(userdata) {
console.log(userdata);
});
}
</script>
</body>
</html>

How do I integrate Microsoft health bot to web application

I have created few scenarios in health bot designer. I am trying to integrate with my front end. However, I don't see any complete documentation around integrate process. I have already referred https://github.com/Microsoft/HealthBot-WebChat without any luck. How do I get directline link for healthbot. I have tried with web bot and able to generate directline but not sure how to link web bot channel to health bot scenario. Any help?
You can integrate the Healthcare bot service into a web application using WebChat. First, you need to get your WebChat Secret from the Healthcare Bot Service Manager. In the pane on the left, click on the integrations blade, select secrets in the drop down options, and copy the webchat_secret.
Once you have the secret, you can request a token from DirectLine and render a WebChat component on your web app. Take a look at the example below.
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Healthcare bot</title>
<script src="https://cdn.botframework.com/botframework-webchat/master/webchat.js"></script>
<style>
html, body { height: 100% }
body { margin: 0 }
#webchat,
#webchat > * {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="webchat" role="main"></div>
<script>
(async function() {
// Note, for the simplicity of this example, we are fetching the DirectLine token here;
// however, it is recommended that you create a backend REST API to generate and manage
// your tokens.
const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate',
{
method: 'POST',
headers: {
'Authorization': `Bearer <WEBCHAT_SECRET>`,
'Content-Type': 'application/json'
},
body: {
// The user id must start with `dl` and should be unique for each user.
User: { Id: 'dl_user_id' }
}
});
const { token } = await res.json();
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
}, document.getElementById('webchat'));
})().catch(err => console.log(err));
</script>
</body>
Note, for the simplicity of this example, we are fetching the DirectLine token here; however, it is recommended that you create a backend REST API to generate and manage your tokens.
Hope this helps!
Found a way to do it. We need to add a model and enable the trigger through Health Bot Management Portal
There is a direct way to trigger a scenario from the front end. If you want to completely rely on your own responses, then you have to turn off the built-in scenarios and call a scenario name in the event post javascript code. Look at the "trigger" element below:
botConnection
.postActivity({
type: "event",
value: {
trigger: "your_scenario_name_here", args: {}
},
from: your_user_name,
name: "BeginDebugScenario"
});

Passing QueryString to Microsoft bot web channel

We have created a bot using ms bot framework and wanted to pass some data to bot which user will not input. Like I was thinking if we can pass query string to below web channel.
https://webchat.botframework.com/embed/myformbot?s=XXXXXX&mycustomfield=text
And from bot api code, I can somehow read this querystring parameter...Ideally I know we don't have control over above webchat link, we are only giving access to bot api. But is this possible or any other way to pass data to bot which is not entered by user ?
We are planning to display web channel url to different sites in iframe and wanted to pass currently browsed url to identify from where user has started conversation.
Thanks
Siddharth
This is easily done using the BotFramework-WebChat library. There are just a few steps to perform to get yourself setup.
First, include a div with an id for the webchat instance of your bot to anchor to. In this example, the id is "webchat".
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Bot Chat</title>
<style>
#webchat,
#webchat>* {
border: 1px solid #333;
float: left;
min-height: 600px;
height: 600px;
position: relative;
width: 460px;
}
</style>
</head>
<body>
<div class="example">
<h2>Back Channel Bot</h2>
<div id="webchat"></div>
Next, you will want to include the following scripts in your html page. Please keep in mind that you should NOT store your secret in the browser or client app. It is included here for simplicity.
Here's what is happening. You use the Direct Line secret (from the channel settings in your Azure bot) to generate a token. The token is used to instantiate the bot webchat control. In this example, the user's location is saved in the store's payload as the activity object. This is sent to the bot on any POST_ACTIVITY event (i.e. the user interacts with the bot).
<script src='https://code.jquery.com/jquery-3.3.1.min.js'></script>
<script src='https://cdn.botframework.com/botframework-webchat/master/webchat.js'></script>
<script src='https://unpkg.com/simple-update-in/dist/simple-update-in.production.min.js'></script>
<script>
(async function () {
// To talk to your bot, you should use the token exchanged using your Direct Line secret.
// You should never put the Direct Line secret in the browser or client app.
const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + secret
},
json: true
});
const { token } = await res.json();
let location = window.location.href;
let store = window.WebChat.createStore(
{},
({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/POST_ACTIVITY') {
// simple-update-in is used to update the "action"
action = window.simpleUpdateIn(action, ['payload', 'activity', 'channelData', 'location'], () => location);
}
return next(action);
}
);
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
styleOptions: {
botAvatarInitials: 'BF',
userAvatarInitials: 'WC'
}
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));;
</script>
</body>
</html>
On the bot side, the bot is listening for the incoming message. The activity object will contain the data you sent and will look something like this:
channelData: { clientActivityID: '15469824216440.emdrovn0f5h', location: 'http://localhost:3000/' }
Hope of help!

Proxied HTTP response being modified

I have a gulpfile that launches a dev proxy server.
gulp.task('dumbserver', ()=> {
const express = require('express');
const httpProxy = require('http-proxy');
const app = express();
const proxy = httpProxy.createProxyServer();
app.use('/api', function (req, res) {
proxy.web(req, res, {target: 'https://bos1-vcd-sp-static-199-8.eng.vmware.com/api', secure: false},
(e) => console.log('error', e)
);
});
return app.listen(8080, function () {
console.log('Server started on port 8080.');
});
});
When I make a call on postman directly to https://bos1-vcd-sp-static-199-8.eng.vmware.com/api/session I get the following payload:
<Session locationId="86171c79-e8f0-4c06-a0a8-4bc7fde76915#7bcf706c-d90f-4e1b-b0cc-b2a13db3e618" org="juan" roles="System Administrator" user="administrator" userId="urn:vcloud:user:7b5f0241-f597-4851-8cae-655a15afde24" href="https://bos1-vcd-sp-static-199-8.eng.vmware.com/api/session" type="application/vnd.vmware.vcloud.session+xml">
<Link rel="down" href="https://bos1-vcd-sp-static-199-8.eng.vmware.com/api/org/" type="application/vnd.vmware.vcloud.orgList+xml"/>
<Link rel="remove" href="https://bos1-vcd-sp-static-199-8.eng.vmware.com/api/session"/>
<Link rel="down" href="https://bos1-vcd-sp-static-199-8.eng.vmware.com/api/admin/" type="application/vnd.vmware.admin.vcloud+xml"/>
<Link rel="down" href="https://bos1-vcd-sp-static-199-8.eng.vmware.com/api/admin/extension" type="application/vnd.vmware.admin.vmwExtension+xml"/>
<Link rel="nsx" href="https://bos1-vcd-sp-static-199-8.eng.vmware.com/network" type="application/xml"/>
<Link rel="openapi" href="https://bos1-vcd-sp-static-199-8.eng.vmware.com/cloudapi" type="application/json"/>
</Session>
However, when I make a call using postman to localhost:8080/api/session, the last two links have the hrefs rewritten to be
<Link rel="nsx" href="https://localhost:8080/network" type="application/xml"/>
<Link rel="openapi" href="https://localhost:8080/cloudapi" type="application/json"/>
But none of the other links have been rewritten.
Any suggestions about what could be going on?
It turns out that http-proxy adds a HOST header. The server side code that was generating the links was using that HOST header if available as the base URL, overriding the configured value.
Not deleting the question in the hopes that the knowing fact that http-proxy adds a host header to requests may cause a bug in your code.

API Call From Word Web Add-In (Office.Js) Is Not Working: CORS Issue?

Friends,
I am trying to call API from Word Add-in and getting "Access Denied" error. I did some research and it looks like "Cross Origin Resource Sharing" is the cause.
1. Web API
I am hosting Web API 2 locally at "http://localhost:61546/api/ORG_NAMES"
& I have enabled CORS to accept all origins, See below WebApiConfig.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
2. Test Application
To test this API to ensure it supports CORS, I have created below page and hosted on localhost:52799/home.html, I was able to get expected response. I have tested this in IE 10 & Chrome.
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
$("button").click(function () {
var obj;
.support.cors = true;
$.getJSON("http://localhost:61546/api/ORG_NAMES/112233",
function (data) {
alert(data.ORG_ID);
});
});
});
</script>
</head>
<body>
<button>Click me</button>
</body>
3. Word Add-In
Now I wanted to call this API from my Word Web Add-In. Word Add-In running from different host https://localhost:44339/, see below code. Here getJSON returns "Access Denied".
var OrgID;
$.getJSON("http://localhost:61546/api/ORG_NAMES/112233",
function (data) {
OrgID = data.ORG_ID;
});
Also when I call API from word add-in, it's not going to fiddler.
Note: This is "Web Add-ins --> Word Add-in" project.
4. Fix - Need Help
Not sure why I am getting "Access Denied" error from Word-Add-In, if CORS is the issue then my test application (#2) shouldn't have worked, correct ?
I have tried call JSON using "$.ajax", "XMLHttpRequest" but it didn't work.I might be missing some configuration settings.
Appreciate any help here.
Let me know if you need more information.
Since it sounds like an issue within an Office Add-in only, rather than in a regular page, have you tried setting your AppDomains in the manifest file? See "Specify domains you want to open in the add-in window" in https://dev.office.com/docs/add-ins/overview/add-in-manifests
<?xml version="1.0" encoding="UTF-8"?>
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="TaskPaneApp">
<Id>c6890c26-5bbb-40ed-a321-37f07909a2f0</Id>
<Version>1.0</Version>
<ProviderName>Contoso, Ltd</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Northwind Traders Excel" />
<Description DefaultValue="Search Northwind Traders data from Excel"/>
<AppDomains>
<AppDomain>https://www.northwindtraders.com</AppDomain>
</AppDomains>
<DefaultSettings>
<SourceLocation DefaultValue="https://www.contoso.com/search_app/Default.aspx" />
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
</OfficeApp>
You will not need Jsonp if you are making Ajax calls. You will have to make sure that you all launches with HTTPS, if it is launching in HTTP it will block that traffic. Remember that office-js back bone is IE and there for; for security purposes the api will only allow HTTPS
Update
Remember that an office-js add in is actually two projects and you must make sure your projects are both launching in HTTPS. Also I would just look over the Manifest file and look at your source and make sure that is point at HTTPS
I had same issue using ajax could not call web-api.NET MVC.
Web api side(Server side):
Implement CORS in Web api because excel office.js works on diffent port and binds proxy object of server inside excel while web api are held on another port so it is as good as having 2 different domains on local so browser automatically blocks request made.
So Cross origin Resource sharing is required.
Enable Https for web apis.
http://csharp-video-tutorials.blogspot.com/2016/09/aspnet-web-api-enable-https.html
Client side
Just make call using ajax as shown below.
url: 'https://localhost:44319/api/Default/PostItems'
Note : https : is compulsory required .
function makeAjaxCall(rangeJSON) {
$.ajax({
url: 'https://localhost:44319/api/Default/PostItems',
type: 'POST',
data: rangeJSON,
contentType: 'application/json;charset=utf-8',
}).done(function (data) {
console.log(data)
app.showNotification(data.Status, data.Message);
}).fail(function (status) {
app.showNotification('Error', 'Could not communicate with the server.');
}).always(showResponse);
}
function exceltojson() {
Excel.run(function (ctx) {
var range = ctx.workbook.worksheets.getItem("Sheet1").getRange("A1:BO765");
range.load("values, numberFormat");
ctx.sync().then(
function () {
makeAjaxCall(JSON.stringify(range.values));
}).catch(function (error) {
console.log(error);
});
});
function showResponse(object) {
console.log(object);
$("#output").text(JSON.stringify(object,null, 4));
}

Resources