Custom Headers with GRPC in .NET Core - grpc

I am working on building a service in GRPC using .NET core. The scenario is as follows - there are 2 services - A and B.
Service A exposes a REST API.
Service B exposes GRPC API.
User invokes Service A, and Service A invokes Service B. I need to pass the headers sent by the user to Service B.
How can I do this with GRPC? The Interceptor interface does not seem to expose the Metadata collection.

To forward the headers sent from client, on the rest api you can do something like this:
using var channel = GrpcChannel.ForAddress("http://localhost:5005");
var client = new Greeter.GreeterClient(channel);
Metadata headers = new()
{
{ "myCustomHeader", myCustomHeader }
};
var reply = await client.SayHelloAsync(new HelloRequest { Name = model.Name }, headers);
Using the Metadata you can pass all headers that you need.
On the gRPC service side, you need to use HttpContextAccessor here
This way you can read the http context request headers
string header = _httpContextAccessor.HttpContext.Request.Headers["myCustomHeader"];
take a look at this repo: here

Related

ASP.NET Core 3.1 Webapi Project returns Origin as null in header

I have a WebApi project to provide data and a ASP.NET Core 3.1 project to call the APIs.
I used Api caller Header fields like HOST, REFERER and ORIGIN to detect the client domain name which called Api to provide proper data, don't want to post/get domain as the parameter from client, just want to other domains call my api and I provide data based on the domain name, I implemented like:
Request.Headers.TryGetValue("Host", out var traceValueHost);
Request.Headers.TryGetValue("Referer", out var traceValueReferer);
Request.Headers.TryGetValue("Origin", out var traceValueOrigin);
But when I publish the code all 3 returns null. Actually I cannot detect the client domain and face errors, what would be the problem? How clients have to get/post to send me their domain names in their Webapi call headers?
The only way I found was adding Origin to the client domain headers
public static async Task<T> GetWebApiAsync<T>(string postUrl, string domain="")
{
HttpClient Client = new HttpClient();
if (domain != "")
Client.DefaultRequestHeaders.TryAddWithoutValidation("Origin", domain);
string result = await Client.GetAsync(postUrl)?
.Result?.Content?.ReadAsStringAsync()??string.Empty;
if (result == string.Empty) return default(T);
return JsonConvert.DeserializeObject<T>(result);
}

How to send message from the js library to a group in Azure SignalR Serverless

Hi I'm trying to send a message to a group using the Azure Signal R Serverless JS Client Js Library.
I can do this from the Azure Serverless Function as simply as:
await signalRMessages.AddAsync(
new SignalRMessage
{
GroupName = m.GroupName,
Target = m.Target,
Arguments = new[] { m.Message }
});
*where signalRMessages = IAsyncCollector signalRMessages
How can I send this same message from the js library?
trying to send a message to a group using the Azure Signal R Serverless
You can refer to this github repo that shows with sample code how to implement group broadcasting functionality in Azure functions with Azure SignalR Service.
Add user to a group using the SignalRGroupAction class
return signalRGroupActions.AddAsync(
new SignalRGroupAction
{
ConnectionId = decodedfConnectionId,
UserId = message.Recipient,
GroupName = message.Groupname,
Action = GroupAction.Add
});
On client side, make request to endpoint to add a user to a group
function addGroup(sender, recipient, connectionId, groupName) {
return axios.post(`${apiBaseUrl}/api/addToGroup`, {
connectionId: connectionId,
recipient: recipient,
groupname: groupName
}, getAxiosConfig()).then(resp => {
if (resp.status == 200) {
confirm("Add Successfully")
}
});
}
Test Result
Updated:
Q: "send the message from the JS Client straight from the socket".
A: From here, we can find:
Although the SignalR SDK allows client applications to invoke backend logic in a SignalR hub, this functionality is not yet supported when you use SignalR Service with Azure Functions. Use HTTP requests to invoke Azure Functions.
It's seems like this is now possible ...
https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-concept-serverless-development-config#sending-messages-from-a-client-to-the-service
Sending messages from a client to the service If you have upstream
configured for your SignalR resource, you can send messages from
client to your Azure Functions using any SignalR client. Here is an
example in JavaScript:
JavaScript
connection.send('method1', 'arg1', 'arg2');

How to consume restful services from another restful service in asp.net?

I have an URL of one restful service, I want to consume this restful service from another restful service.
Suppose URL is first rest service is "http://testapi.com/services/rest/?method=getList&key=123”
Restful service 1 - > Restful service 2 -> asp.net client application
Could you provide any example with code and configuration settings.
Thanks
You can use the HttpClient. The example in the post is using a console application, but you can still use it from a Web Api project (which I have on some of my projects).
Example get async:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("YOURURIHERE");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// New code:
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync>Product>();
}
}

WebSockets work without valid authorization token (Spring, SockJS, STOMP, OAuth)

I am integrating with WebSockets in my Spring MVC application. The authentication mechanism for my application is OAuth.
I was able to pass my OAuth token in connection string when connecting to SockJS:
var webSocketUrl = '/websocket' + '?access_token=' + auth.access_token;
var socket = new SockJS(webSocketUrl);
var stompClient = Stomp.over(socket);
Now I can send messages and subscribe to STOMP channels:
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/greetings', function(greeting){
console.log(greeting);
});
stompClient.send("/app/hello", {}, JSON.stringify('John'));
});
In my backend I am able to get user principle injected to my STOMP controller methods (which means that Spring understands that there is an OAuth token in connection string):
#Controller
public class MyWebsocketsController {
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public String greet(String name, Principal authorizedUser) {
return "Hello, " + name + ", you have authorized as " + authorizedUser.getName();
}
}
Now I would like to require user authorization on all messages and subscriptions, i.e. I would like to make sure that all calls to web sockets return 403 error code if no valid token was provided when connecting to SockJS.
I add this security configuration to my project:
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(CONNECT, HEARTBEAT, UNSUBSCRIBE, DISCONNECT).permitAll()
.simpDestMatchers("/app/**").authenticated()
.simpSubscribeDestMatchers("/topic/**").authenticated()
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll()
.anyMessage().denyAll();
}
}
But it does not seem to do the job. If I remove access token from connection string, I am still able to send messages to controller and to subscribe to channel.
// This stil works:
var webSocketUrl = '/websocket'; // + '?access_token=' + auth.access_token;
Of course, now I can't get the user principle in my controller, but except for this web sockets work fine.
I would appreciate any ideas how to make this thing work or any explanation why web sockets security configuration is not working for my case. What am I missing? Thanks.

ASP.NET 5 SignalR not working with .NET client

I work on a ASP.NET 5 (ASP.NET vNext) website. I use SignalR server (1.0 Beta 3) for some processing. It it is correctly set up because I can successfully invoke server methods from a Javascript browser client.
But when a I use .NET client (.NET 4.5 with SignalR 2.2.0), the method invoke fail with the generic "error 500".
I have downloaded both SignalR server and client sources to be able to debug them. I have seen that DefaultHttpClient.Post() client method is called with valid "postData" parameter, but the server PersistentConnectionMiddleware.Invoke() method has a http context without any "Form" value inside the request. And it makes SignalR server side failing in the ForeverTransport.ProcessSendRequest() method.
The post form seems to be forgotten during the transfer between the client and the server (I use the default IIS Express server).
Any idea? Thank you...
I saw the issue you opened and have committed a fix/workaround.
At the moment it doesn't look like SignalR 3 works with the client due to the fact it expects all requests to be form encoded. The workaround is to update ProcessSendRequest() so it can get the non-form-encoded data from the 2.2 .NET client;
protected virtual async Task ProcessSendRequest()
{
var data = await GetData().PreserveCulture();
if (Received != null)
{
await Received(data).PreserveCulture();
}
}
private async Task<string> GetData()
{
if (Context.Request.HasFormContentType)
{
var form = await Context.Request.ReadFormAsync().PreserveCulture();
return form["data"];
}
else
{
var stream = new System.IO.StreamReader(Context.Request.Body);
var output = await stream.ReadToEndAsync().PreserveCulture();
var decoded = UrlDecoder.UrlDecode(output);
return decoded.Replace("data=", "");
}
}

Resources