Microsoft bot framework: What must a messaging endpoint return? - rest

I am implementing my own messaging endpoint for a MS Teams bot from scratch. I'm almost there. The endpoint does get called with conversationUpdate events, but I see:
There was an error sending this message to your bot: HTTP status code
BadRequest
in the admin on https://dev.botframework.com/bots/channels?id=...
I am probably returning something bad in the HTTP request. As I didn't find anything about the response in the REST API docs, I am just sending the string "{}" with a standard content type.
So what do I actually need to return?
Edit: It appears that the relevant part of the botbuilder-java package is this function in ControllerBase.java:
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
try {
Activity activity = getActivity(request);
String authHeader = request.getHeader("Authorization");
adapter.processIncomingActivity(
authHeader, activity, turnContext -> bot.onTurn(turnContext)
).handle((result, exception) -> {
if (exception == null) {
response.setStatus(HttpServletResponse.SC_ACCEPTED);
return null;
}
if (exception.getCause() instanceof AuthenticationException) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
return null;
});
} catch (Exception ex) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
As far as I can tell, this only sets a return code (202) but does not return any content. I now try to do the same thing in my messaging endpoint, but Teams still complains about "BadRequest".
Edit: I have snooped what the actual BotFramework Java sample does - it just returns a code 202 with an empty request body and no content type. I'm now doing the exact same thing, and Teams still complains that it could not send the message. Kinda giving up here.

Related

how to get a valid response from a php web service on GWT

I'm trying to get data from a php web service (that I've put in my localhost - tested and works fine) in the client side of my GWT application.
I've tried to use the following package com.google.gwt.http.client.* it looks like the code works fine but the response is always 0, it's highly likely to be a corss problem but I still can't figure how to solve it even though I've tried to use requestBuilder.setHeader(..);
here's the code I'm working on:
String url = "http://localhost/geoTrackerTest.php?id=15";
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
requestBuilder.setHeader("Access-Control-Allow-Origin", "http://localhost");
requestBuilder.setHeader("Access-Control-Allow-Methods", "POST, GET, UPDATE, OPTIONS");
requestBuilder.setHeader("Access-Control-Allow-Headers", "x-http-method-override");
try {
Request request = requestBuilder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
GWT.log("Error: "+exception.getMessage());
}
public void onResponseReceived(Request request, Response response) {
if (200 == response.getStatusCode()) {
GWT.log("response: "+response.getText());
} else {
GWT.log("response code: "+response.getStatusCode());
}
}
});
} catch (RequestException e) {
GWT.log("Request Exception: "+e.getMessage());
}
I'm still getting 0 as a response.
You will need to set the header in response from server side (not from the GWT client side), then you can make Cross Site Requests from GWT RequestBuilder. Something like this on server side:
Response.setHeader("Access-Control-Allow-Origin","http://localhost");
If you only need to send GET requests, you can use JSONP (http://www.gwtproject.org/javadoc/latest/com/google/gwt/jsonp/client/JsonpRequestBuilder.html) instead to send cross domain requests, without headers setting on server side.

Unable to set values in the map using webclient call response

I am unable to get values filled in the map after making a web client call and using the response of the previous Mono.Here is the code I have tried.The value of parameters.size() comes out to zero.Not able to get the reason as to why the value is not filled.I basically want to return age ( and not Mono object)
from this method.Using block gives an error block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3.
Map<String, String> parameters = new HashMap<String,String>();
Mono<Person> obj = webClient
.post()
.uri("dummy url")
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(Person.class)
.flatMap(resp -> {
parameters.put("name", resp.getName());
parameters.put("age", resp.getAge());
return Mono.just(new Person(resp.getName(),resp.getAge()));
}
);
System.out.println(parameters.size());
Please suggest where I am wrong and solution to fix the same.
Since this is about collecting and using a token of some sort collected from a previous HTTP call, your best bet is to delegate all that to an ExchangeFilterFunction.
An ExchangeFilterFunction is a filter that is executed on the client side for each outgoing request. Here is a very, very naïve implementation of such a filter:
class TokenFilterFunction implements ExchangeFilterFunction {
private final AtomicReference<String> token = new AtomicReference<>();
#Override
public Mono<ClientResponse> filter(ClientRequest req, ExchangeFunction next) {
if (this.token.get() == null) {
return fetchToken(next).then(sendRequest(req, next));
}
else {
return sendRequest(req, next);
}
}
private Mono<ClientResponse> sendRequest(ClientRequest req, ExchangeFunction next) {
ClientRequest request = ClientRequest.from(req)
.header("Token", this.token.get()).build();
return next.exchange(request);
}
private Mono<Void> fetchToken(ExchangeFunction next) {
ClientRequest tokenRequest = ClientRequest.create(HttpMethod.GET,
URI.create("https://example.com/token")).build();
return next.exchange(tokenRequest).doOnNext(res -> {
this.token.set(res.headers().header("Token").get(0));
}).then();
}
}
This could automatically call the token endpoint to fetch a token when needed and directly chain with the request you asked in the first place. Again, such an implementation should be much more complex than that, handling domains, errors, and more.
If you're using some authentication technology, such a filter might be implemented already in Spring Security in a much, much better way.
You can configure it on your client during the building phase, like:
WebClient webClient = WebClient.builder().filter(new TokenFilterFunction()).build();

How to make a rest api call in microsoft bot framework

I need a bot that takes users input, uses it as an id to some third party rest api call and posts back a response. I've looked through Microsoft documentation but didn't find any examples on how to program that request-response process.
Any examples or useful links would be appreciated
Adding to Jason's answer, since you wanted to make a REST api call, take a look at this code :
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
// User message
string userMessage = activity.Text;
try
{
using (HttpClient client = new HttpClient())
{
//Assuming that the api takes the user message as a query paramater
string RequestURI = "YOUR_THIRD_PARTY_REST_API_URL?query=" + userMessage ;
HttpResponseMessage responsemMsg = await client.GetAsync(RequestURI);
if (responsemMsg.IsSuccessStatusCode)
{
var apiResponse = await responsemMsg.Content.ReadAsStringAsync();
//Post the API response to bot again
await context.PostAsync($"Response is {apiResponse}");
}
}
}
catch (Exception ex)
{
}
context.Wait(MessageReceivedAsync);
}
}
Once you get the input from user, you can make a REST call and then after you get the response back from API, post it back to the user using the context.PostAsync method.
As Ashwin said, A bot is just a web API and you are just sending/receiving requests as you would with any web API. Below is some documentation that should help get you started.
Basic Overview
Create a bot with the Bot Connector service
API Reference

What i have to return after a DELETE?

I'm wondering what I have to return after that I call my REST API using the DELETE method. I wasn't able to find out any standard/best practice for this. At the moment my code base use 2 different approach, first of all return the deleted resource so into the Response Body I return just null. The second approach (which I don't really like) I instance a new Object and I return it. What you do think is the best way? If none of this two seems good to you, which one would be the best (practice) approach?
Here a sample of what I actually have: code sample
NB: Of course both of the described approach, are performed after the actual deleting on the DB.
After successful deletion you should return empty body and 204 No Content status code.
When returning 200 OK with empty body some clients (e.g. EmberJS) fail because they expect some content to be parsed.
I would return a HTTP 204 OK in order to signal that the request has succeeded.
If you need to return a response body, in case the deletion has triggered something, I would use a HTTP 200 OK with a body attached.
what about Returning void wich means HTTP 200 OK in case of success
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public void delete(#PathVariable("id") Long id) {
service.delete(id);
}
EDIT
In the front Controller you can use something like this:
#RequestMapping(...)
public ModelAndView deleteMySlide(Model model,...){
try {
//invoke your webservice Here if success
return new ModelAndView("redirect:/anotherPage?success=true");
} catch (HttpClientErrorException e) {
//if failure
return new ModelAndView("redirect:/anotherPage?success=false");
}
}
or :
#RequestMapping(...)
public String deleteMySlide(Model model,...){
try {
//invoke your webservice Here if success
model.addAttribute("message","sample success");
return "redirect:/successPage");;
} catch (HttpClientErrorException e) {
//if failure
model.addAttribute("message","sample failure");
return "redirect:/failurePage");
}
}

asp.net Web Api - Default Error Messages

Is there a way of changing Web Api's default behavior for error messages, such as:
GET /trips/abc
Responds with (paraphrased):
HTTP 500 Bad Request
{
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'tripId' of non-nullable type 'System.Guid' for method 'System.Net.Http.HttpResponseMessage GetTrip(System.Guid)' in 'Controllers.TripController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
I'd like to avoid giving out this rather detailled information about my code, and instead replace it with something like:
HTTP 500 Bad Request
{
error: true,
error_message: "invalid parameter"
}
I'd be able to do this inside the UserController, but the code execution doesn't even go that far.
edit:
I've found a way of removing detailed error messages from the output, using this line of code in Global.asax.cs:
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.LocalOnly;
This produces a message like this:
{
"Message": "The request is invalid."
}
which is better, however not exactly what I want - We've specified a number of numeric error codes, which are mapped to detailed error messages client-side. I would like to only output the appropriate error code (that I'm able to select prior to output, preferrably by seeing what kind of exception occured), for example:
{ error: true, error_code: 51 }
You might want to keep the shape of the data as the type HttpError even if you want to hide detailed information about the actual exception. To do that, you can add a custom DelegatingHandler to modify the HttpError that your service throws.
Here is a sample of how the DelegatingHandler might look like:
public class CustomModifyingErrorMessageDelegatingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) =>
{
HttpResponseMessage response = responseToCompleteTask.Result;
HttpError error = null;
if (response.TryGetContentValue<HttpError>(out error))
{
error.Message = "Your Customized Error Message";
// etc...
}
return response;
});
}
}
Maggie's answer worked for me as well. Thanks for posting!
Just wanted to some bits to her code for additional clarification:
HttpResponseMessage response = responseToCompleteTask.Result;
HttpError error = null;
if ((!response.IsSuccessStatusCode) && (response.TryGetContentValue(out error)))
{
// Build new custom from underlying HttpError object.
var errorResp = new MyErrorResponse();
// Replace outgoing response's content with our custom response
// while keeping the requested MediaType [formatter].
var content = (ObjectContent)response.Content;
response.Content = new ObjectContent(typeof (MyErrorResponse), errorResp, content.Formatter);
}
return response;
Where:
public class MyErrorResponse
{
public MyErrorResponse()
{
Error = true;
Code = 0;
}
public bool Error { get; set; }
public int Code { get; set; }
}