I am new to Helidon SE and would like to know if there is a way to implement q params in REST service created via Helidon SE.
Any help in this regard is truly appreciated.
Thanks,
Gaurav
If you want to use and read params in the following way e.g.
http://localhost:8080/?q=test&k=test2
Then -in case of Helidon SE- do the following to get those parameters:
private void getParam(ServerRequest request, ServerResponse response) {
Map params = request.queryParams().toMap();
logger.info("params: " + params);
logger.info("q: " + params.get("q"));
logger.info("k: " + params.get("k"));
...
}
Obviously the getParam method is configured for "/" path.
How do I retrieve query parameters in a Helidon SE application:
Here is one example,
HelloService.java
public class HelloService implements Service {
private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
HelloService(Config config) {
}
#Override
public void update(Rules rules) {
rules.get("/", this::getDefaultMessageHandler);
}
private void getDefaultMessageHandler(ServerRequest request, ServerResponse response) {
var params = request.queryParams().toMap();
System.out.println("name: " + params.get("name").get(0));
System.out.println("email: " + params.get("email").get(0));
sendResponse(response, params.get("name").get(0));
}
private void sendResponse(ServerResponse response, String name) {
var returnObject = JSON.createObjectBuilder().add("name", name).build();
response.send(returnObject);
}
}
Main
public class HelidonSeRestHelloWorldApplication {
public static void main(final String[] args) {
startServer();
}
private static Single<WebServer> startServer() {
var config = Config.create();
var server = WebServer.builder(createRouting(config)).config(config.get("server"))
.addMediaSupport(JsonpSupport.create()).build();
var webserver = server.start();
webserver.thenAccept(ws -> {
System.out.println("Web server started! http://localhost:" + ws.port() + "/hello");
ws.whenShutdown().thenRun(() -> System.out.println("Web server is down!"));
}).exceptionallyAccept(t -> {
System.out.println("Web startup failed: " + t.getMessage());
});
return webserver;
}
private static Routing createRouting(Config config) {
var helloService = new HelloService(config);
return Routing.builder()
.register("/hello", helloService).build();
}
}
application.properties
server.port=9080
Run the application and hit http://localhost:9080/hello?name=alpha&email=alpha#alpha.alpha
Console output:
name: alpha
email: alpha#alpha.alpha
++ How do I retrieve path parameters in a Helidon SE application:
Main
public class HelidonSeRestHelloWorldApplication {
...........................
...........................
private static Routing createRouting(Config config) {
var helloService = new HelloService(config);
return Routing.builder()
.register("/hello/{name}", helloService).build();
}
}
HelloService.java
public class HelloService implements Service {
............
...........
private void getDefaultMessageHandler(ServerRequest request, ServerResponse response) {
var name = request.path().absolute().param("name");
System.out.println(name);
sendResponse(response, name);
}
.................
.................
}
Run the application and hit: http://localhost:9080/hello/alpha
Related
A SOAP Web-service, which accepts request in following format -
<?xml version = "1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
xmlns:ns="http://...." xmlns:ns1="http://...." xmlns:ns2="http://...."
xmlns:ns3="http://....">
<SOAP-ENV:Header>
<ns:EMContext>
<messageId>1</messageId>
<refToMessageId>ABC123</refToMessageId>
<session>
<sessionId>3</sessionId>
<sessionSequenceNumber>2021-02-24T00:00:00.000+5:00</sessionSequenceNumber>
</session>
<invokerRef>CRS</invokerRef>
</ns:EMContext>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:getEmployee>
<ns:empId>111</ns:empId>
</ns1:getEmployee>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
When trying to make a SOAP request to it using JAXB2, it is giving org.springframework.ws.soap.client.SoapFaultClientException: EMContext Header is missing
I am using
pring-boot-starter
spring-boot-starter-web-services
org.jvnet.jaxb2.maven2 : maven-jaxb2-plugin : 0.14.0
and
Client -
public class MyClient extends WebServiceGatewaySupport {
public GetEmployeeResponse getEmployee(String url, Object request){
GetEmployeeResponse res = (GetEmployeeResponse) getWebServiceTemplate().marshalSendAndReceive(url, request);
return res;
}
}
Configuration -
#Configuration
public class EmpConfig {
#Bean
public Jaxb2Marshaller marshaller(){
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPath("com.crsardar.java.soap.client.request");
return jaxb2Marshaller;
}
#Bean
public MyClient getClient(Jaxb2Marshaller jaxb2Marshaller){
MyClient myClient = new MyClient();
myClient.setDefaultUri("http://localhost:8080/ws");
myClient.setMarshaller(jaxb2Marshaller);
myClient.setUnmarshaller(jaxb2Marshaller);
return myClient;
}
}
App -
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Bean
CommandLineRunner lookup(MyClient myClient){
return args -> {
GetEmployeeRequest getEmployeeRequest = new GetEmployeeRequest();
getEmployeeRequest.setId(1);
GetEmployeeResponse employee = myClient.getEmployee("http://localhost:8080/ws", getEmployeeRequest);
System.out.println("Response = " + employee.getEmployeeDetails().getName());
};
}
}
How can I add EMContext Header to the SOAP request?
The server is complaining because your Web Service client is not sending the EMContext SOAP header in your SOAP message.
Unfortunately, currently Spring Web Services lack of support for including SOAP headers in a similar way as the SOAP body information is processed using JAXB, for example.
As a workaround, you can use WebServiceMessageCallback. From the docs:
To accommodate the setting of SOAP headers and other settings on the message, the WebServiceMessageCallback interface gives you access to the message after it has been created, but before it is sent.
In your case, you can use something like:
public class MyClient extends WebServiceGatewaySupport {
public GetEmployeeResponse getEmployee(String url, Object request){
// Obtain the required information
String messageId = "1";
String refToMessageId = "ABC123";
String sessionId = "3";
String sessionSequenceNumber = "2021-02-24T00:00:00.000+5:00";
String invokerRef = "CRS";
GetEmployeeResponse res = (GetEmployeeResponse) this.getWebServiceTemplate().marshalSendAndReceive(url, request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
// Include the SOAP header content for EMContext
try {
SoapMessage soapMessage = (SoapMessage)message;
SoapHeader header = soapMessage.getSoapHeader();
StringSource headerSource = new StringSource(
"<EMContext xmlns:ns=\"http://....\">" +
"<messageId>" + messageId + "</messageId>" +
"<refToMessageId>" + refToMessageId + "</refToMessageId>" +
"<session>" +
"<sessionId>" + sessionId + "</sessionId>" +
"<sessionSequenceNumber>" + sessionSequenceNumber + "</sessionSequenceNumber>" +
"</session>" +
"<invokerRef>" + invokerRef + "</invokerRef>" +
"</EMContext>"
);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
} catch (Exception e) {
// handle the exception as appropriate
e.printStackTrace();
}
}
});
return res;
}
}
Similar questions have been posted in SO. Consider for instance review this or this other.
Java JAX-RS web service with Jersey / Jackson, a service method expects a User parameter (POJO) as JSON. The client app (Angular 6) sends a POST request containing the User parameter (serialized as JSON). The service method call fails with error message: "Unrecognized token 'jsonUser': was expecting ('true', 'false' or 'null')".
Here is the User class (POJO) - you can see I tried annotating all the properties with #JsonProperty, but it's unnecessary, as I'm not "renaming" them:
import java.io.Serializable;
import javax.ws.rs.FormParam;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
//import org.glassfish.jersey.media.multipart.FormDataParam;
/**
* JavaBean for passing the User properties between the UI app (Angular)
* and TearsWs. Implementation requires this to be serializable (JSON).
*/
#JsonIgnoreProperties({ "DELIM" })
public class User implements Serializable {
private String userName;
private String employeeId;
private String employeeName;
private String homeUnitCode;
private boolean certifier;
private HomeUnit[] tkHomeUnits;
private boolean supervisor;
private Employee[] whoISupervise;
private boolean hrStaff;
private boolean collector;
private final static String DELIM = ", ";
public User() {
}
// getters / setters
//#JsonProperty("userName")
public void setUserName(String ldapUid) {
this.userName = ldapUid;
}
public String getUserName() {
return this.userName;
}
//#JsonProperty("employeeId")
public void setEmployeeId(String employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeId() {
return this.employeeId;
}
//#JsonProperty("employeeName")
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getEmployeeName() {
return this.employeeName;
}
//#JsonProperty("homeUnitCode")
public void setHomeUnitCode(String homeUnitCode) {
this.homeUnitCode = homeUnitCode;
}
public String getHomeUnitCode() {
return this.homeUnitCode;
}
//#JsonProperty("certifier")
public void setCertifier(boolean certifier) {
this.certifier = certifier;
}
public boolean getCertifier() {
return this.certifier;
}
//#JsonProperty("tkHomeUnits")
public void setTkHomeUnits(HomeUnit[] tkHomeUnitCodes) {
this.tkHomeUnits = tkHomeUnitCodes;
}
public HomeUnit[] getTkHomeUnits() {
return this.tkHomeUnits;
}
//#JsonProperty("supervisor")
public void setSupervisor(boolean supervisor) {
this.supervisor = supervisor;
}
public boolean isSupervisor() {
return this.supervisor;
}
//#JsonProperty("whoISupervise")
public void setWhoISupervise(Employee[] whoISupervise) {
this.whoISupervise = whoISupervise;
}
public Employee[] getWhoISupervise() {
return this.whoISupervise;
}
//#JsonProperty("hrStaff")
public void setHrStaff(boolean hrStaff) {
this.hrStaff = hrStaff;
}
public boolean isHrStaff() {
return this.hrStaff;
}
//#JsonProperty("collector")
public void setCollector(boolean collector) {
this.collector = collector;
}
public boolean isCollector() {
return this.collector;
}
//methods
public boolean hasTauthority() {
return this.certifier || this.collector;
}
public String toString() {
int tkHUs = (tkHomeUnits == null) ? 0 : tkHomeUnits.length;
return "[User: "
+ "userName=" + this.userName + DELIM
+ "employeeId=" + this.employeeId + DELIM
+ "employeeName=" + this.employeeName + DELIM
+ "homeUnitCode=" + this.homeUnitCode + DELIM
+ "certifier=" + this.certifier + DELIM
+ "hrStaff=" + this.hrStaff + DELIM
+ "collector=" + this.collector + DELIM
+ "I can certify " + tkHUs + " homeUnits" + "]";
}
}
Here is the (Java) service method, which should accept and process the POST request:
/**
* Web service method.
*/
#POST
#Path("getTkHomeUnitEmployees")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response getTkHomeUnitEmployees(User user, #HeaderParam("X-Request-Param") String homeUnitCode) throws Exception {
String exceptionMessage;
if (user == null) {
exceptionMessage = "getTkHomeUnitEmployees() received a null User.";
log.error(exceptionMessage);
Response response = Response
.status(500)
.entity(exceptionMessage)
.build();
return response;
}
if (homeUnitCode == null || homeUnitCode.equals("")) {
exceptionMessage = "getTkHomeUnitEmployees() received a null HomeUnitCode.";
log.error(exceptionMessage);
Response response = Response
.status(500)
.entity(exceptionMessage)
.build();
return response;
}
if (!user.hasTauthority()) {
exceptionMessage = "getTkHomeUnitEmployees() received a request from a non-timekeeper and non-collector.";
log.error(exceptionMessage);
Response response = Response
.status(500)
.entity(exceptionMessage)
.build();
return response;
}
try {
Employee[] tkHomeUnitEmployees = new SecurityDao().getTkHomeUnitEmployees(user.getEmployeeId(), homeUnitCode);
Response response = Response
.ok(tkHomeUnitEmployees)
.header("Access-Control-Allow-Origin", "*")
.build();
return response;
} catch (Exception ex) {
exceptionMessage = "getTkHomeUnitEmployees(): " + ex;
Response response = Response
.status(500)
.entity(exceptionMessage)
.build();
return response;
}
}
The User object (client side, Javascript) is converted to JSON and encapsulated as a parameter in HttpParams; the POST passes it in the body of the request.
Here is the (Angular) client method, which sends the POST request to the web service:
getTkHomeUnitEmployees(user: User, homeUnitCode: string): Observable<Employee[]> {
const headers = new HttpHeaders()
.set('Content-Type', 'application/json')
.set('X-Request-Param', homeUnitCode); // homeUnitCode parameter in HttpHeaders
const httpOptions = {
headers: headers
};
let jsonUser: string = JSON.stringify(user);
const httpParams = new HttpParams()
.set('jsonUser', jsonUser);
let postUrl = this.wsUrl + 'getTkHomeUnitEmployees';
//postUrl += '?homeUnitCode=' + homeUnitCode; // homeUnitCode parameter as QueryParam
let obsArrayEmployees: Observable<Employee[]> = this.httpClient.post<Employee[]>(postUrl, httpParams, httpOptions);
return obsArrayEmployees;
}
...here I'm debugging the client (# browser Dev Tools), with a break in the getTkHomeUnitEmployees() method:
...I've displayed the value of jsonUser in the Console:
...here is the error in the Response:
...and here is the Request Params.
So, it appears the Jackson JsonParser is attempting to read and parse the parameter sent in the request, but the parameter includes "jsonUser=" at the beginning as part of it's value (json to be parsed). This is clearly wrong...
The service method blows up before actually entering / processing code; I can't set a breakpoint within the service method to examine the value of the parameter. It behaves as a "parameter invalid, return to caller" response.
I thought to manually hack the "jsonUser=" out of it (# client side), but it's not there. At the client, "jsonUser=" is not part of the parameter value; I believe it's just the key=value syntax of an http parameter (parameter-name=parameter-value), perhaps it's being prepended when the parameter is encapsulated into the HttpParams object.
Obviously I'm doing something wrong, but I haven't been able to figure it out; I thought this was the correct way to do this, but apparently not. Hope someone can help soon, I've been stuck on this for a couple days already.
You don't need to covert the 'user' object to string to pass to backend. Try passing the user object as it is.
this.httpClient.post<Employee[]>(postUrl, user, httpOptions);
And also please check if parameters passed really match the rest service exposed.
I have implemented a zuul gateway service for the communication between some micro services that i have wrote. I have a specific scenario like i want to change the service path in one of my custom filter and redirected to some other service. Is this possible with the zuul gateway?. I have tried putting "requestURI" parameter with the updated uri to the request context in my route filter but that didn't worked out well
Please help me out guys
thanks in advance
yes, you can. for that you need to implement ZuulFilter with type PRE_TYPE, and update response with specified Location header and response status either 301 or 302.
#Slf4j
public class CustomRedirectFilter extends ZuulFilter {
#Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
#Override
public int filterOrder() {
return FilterConstants.SEND_FORWARD_FILTER_ORDER;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
String requestUrl = ctx.getRequest().getRequestURL().toString();
if (shouldBeRedirected(requestUrl)) {
String redirectUrl = generateRedirectUrl(ctx.getRequest());
sendRedirect(ctx.getResponse(), redirectUrl);
}
return null;
}
private void sendRedirect(HttpServletResponse response, String redirectUrl){
try {
response.setHeader(HttpHeaders.LOCATION, redirectUrl);
response.setStatus(HttpStatus.MOVED_PERMANENTLY.value());
response.flushBuffer();
} catch (IOException ex) {
log.error("Could not redirect to: " + redirectUrl, ex);
}
}
private boolean shouldBeRedirected(String requestUrl) {
// your logic whether should we redirect request or not
return true;
}
private String generateRedirectUrl(HttpServletRequest request) {
String queryParams = request.getQueryString();
String currentUrl = request.getRequestURL().toString() + (queryParams == null ? "" : ("?" + queryParams));
// update url
return updatedUrl;
}
}
I´m trying to create a client for some Rest application. I tested the Rest application with Advanced REST client in Chrome.
I received:
{
"authorization": false,
"provider": "Provider"
}
That is okey.
but I would like obtain this in my client:
public class MainApp extends Application {
public static final String BASE_URI = "http://localhost:8000/test";
public static final String PATH_NAME= "/sytem/Consumer/r/Provider";
#Override
public void start(Stage stage) throws Exception {
}
public static void main(String[] args) {
Client client = ClientBuilder.newClient();
WebTarget target = client.target(BASE_URI).path(PATH_NAME);
Response response = target.request(MediaType.APPLICATION_JSON).get();
System.out.println(response);
client.close();
}
}
I only receive this in the terminal:
λ java -jar Client_consumer-1.0-SNAPSHOT.jar
org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine$1#6591f517
The class is :
#XmlRootElement
public class Auth_response implements Serializable {
private String Provider;
private boolean authorization;
public Auth_response() {
super();
}
public Auth_response(String Provider, boolean authorization) {
super();
this.Provider = Provider;
this.authorization = authorization;
}
public String getProvider() {
return Provider;
}
public boolean isAuthorization() {
return authorization;
}
public void setProvider(String Provider) {
this.Provider = Provider;
}
public void setAuthorization(boolean authorization) {
this.authorization = authorization;
}
#Override
public String toString() {
return "Auth_response{" + "Provider=" + Provider + ", authorization=" + authorization + '}';
}
}
I would like to know how to print properly the response, and how to convert to a java object again. I tried with some examples, but nothing works and I think that I need some guide.
EDIT:
I tried this for getting the object:
Auth_response r=
client.target("http://localhost:8000/test/system/Consumer/r/Provider")
.request(MediaType.APPLICATION_JSON_TYPE).
get(Auth_response.class);
System.out.println("funciona:");
System.out.println(r.getProvider());
System.out.println(r.isAuthorization());
But I obtain the next error:
Caused by: javax.ws.rs.ProcessingException: RESTEASY003145: Unable to find a MessageBodyReader of content-type application/json and type class ah.client_consumer.Auth_response
at org.jboss.resteasy.core.interception.ClientReaderInterceptorContext.throwReaderNotFound(ClientReaderInterceptorContext.java:42)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:75)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:52)
at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.aroundReadFrom(GZIPDecodingInterceptor.java:59)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:55)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:251)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readEntity(ClientResponse.java:181)
at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:213)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:105)
... 14 more
Exception running application ah.client_consumer.MainApp
Try changing below line :
Response response = target.request(MediaType.APPLICATION_JSON).get();
System.out.println(response);
To
Response response = target.request(MediaType.APPLICATION_JSON).get(String.class);
System.out.println(response.toString());
Because you have not specified what type of response you are accepting, you are getting object as response.
SOLUTION:
Add to the code:
ResteasyProviderFactory instance=ResteasyProviderFactory.getInstance();
RegisterBuiltin.register(instance);
instance.registerProvider(ResteasyJacksonProvider.class);
The solution was finding in : Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String
I have the following code to create a netty web server based on http server created in the netty's example. My buisness logic is the following.
public class HttpServerHandler extends SimpleChannelInboundHandler<Object> {
private final static Logger LOG = LogManager
.getLogger(HttpServerHandler.class);
private WorkflowService workflowService;
private HttpRequest request;
private final StringBuffer buff = new StringBuffer();
private API avalancheApi;
public HttpServerHandler(WorkflowService workflowService) {
this.workflowService = workflowService;
this.avalancheApi = new API(this.workflowService);
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
LOG.debug("channelActive");
LOG.debug(ctx.toString());
};
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
#Override
public void channelRead0(ChannelHandlerContext ctx, Object msg)
throws IOException {
avalancheApi.setContext(ctx);
if (msg instanceof HttpRequest) {
HttpRequest request = this.request = (HttpRequest) msg;
if (HttpHeaders.is100ContinueExpected(request)) {
send100Continue(ctx);
}
String command = getCommand(request);
LOG.debug(command);
Map<String, List<String>> parameters = getParameters(request);
LOG.debug(parameters);
switch (command) {
case "/login":
ctx = avalancheApi.login(parameters);
break;
case "/test":
ctx = avalancheApi.test();
break;
default:
break;
}
}
if (msg instanceof LastHttpContent) {
LOG.debug("msg is of LastHttpContent");
}
if (!HttpHeaders.isKeepAlive(request)) {
// If keep-alive is off, close the connection once the content is
// fully written.
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(
ChannelFutureListener.CLOSE);
}
}
public class API {
private static final Logger LOG = LogManager.getLogger(API.class);
private ChannelHandlerContext ctx;
private HttpResponse response;
private WorkflowService workflowService;
public API(WorkflowService workflowService) {
this.workflowService = workflowService;
this.ctx = null;
}
public void setContext(ChannelHandlerContext ctx) {
this.ctx = ctx;
}
public ChannelHandlerContext login(Map<String, List<String>> parameters) {
boolean success;
String username = getUsername(parameters);
String password = getPassword(parameters);
User user = null;
user = workflowService.login(username, password);
success = validateLogin(user);
this.response = writeLoginResponse(success);
this.ctx.write(this.response);
writeLoginContext(success, response);
return this.ctx;
}
private void writeLoginContext(boolean success, HttpResponse response) {
JsonObject jsonResponseMessage = new JsonObject();
jsonResponseMessage.addProperty("result", success);
LOG.debug(jsonResponseMessage.toString());
this.ctx.write(Unpooled.copiedBuffer(jsonResponseMessage.toString(),
CharsetUtil.UTF_8));
this.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
jsonResponseMessage.toString().length());
}
private HttpResponse writeLoginResponse(boolean success) {
if (success)
return createSuccessfullLoginResponse();
else
return createLoginFailureResponse();
}
private HttpResponse createLoginFailureResponse() {
return Response.loginFailureResponse();
}
private HttpResponse createSuccessfullLoginResponse() {
return Response.loginSuccessResponse();
}
}
Response class is only creating the response and the content_type which is of application/json. Content Length is set in the API class. Using python client with requests, results in the request made in http://localhost/login?username=name&password=pass works only once. The second time everything works, but it doesn't finish processing the request and send the response object. Api calls get executed normally, and I also get the message of LastHttpContext message getting print. The problem sometimes happens with browser too. Am I missing something? Maybe the content data and the content length doesn't match? Could it be that when making requests from python client, the content of the previous context isn't flushed and the content_length value of the header and content length of the context doesn't match?
Just wild guess
this.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
jsonResponseMessage.toString().length());
Instead, shouldn't you be doing jsonResponseMessage.toString().getBytes().length ?? Sometimes, one character is not just one byte.
My guess is that you have overwritten the context in your API class, and as a result, are writing the response to the wrong context. Is your HttpServerHandler marked with #Shareable?