How can I return JSON with HTTP code in Vibe.d? - d

I would like to return not only JSON, but also the HTTP response code.
I registering REST interface through URLRouter:
router.registerRestInterface(new ClientServerAPI);
Example of my REST implementation:
module clienserverapi.clientserver;
import api.clientserver;
import models.replies.client_versions;
/**
Implementation of Client-Server API.
*/
class ClientServerAPI : IClientServerAPI {
#safe:
ClientVersions getSupportedClientVersions() {
bool[string] unstableFeatures;
return ClientVersions(supportedVersions.dup, unstableFeatures);
}
}

In the REST interface generator the response codes are handled automatically and as you can't pass in a HTTPServerResponse/HTTPServerRequest argument into your REST methods, you can't control what status gets returned.
However there are some built-in statuses which get handled:
200/204 are returned based on content
400 Bad request for mismatched arguments
404 Not found for unmatched routes
500 internal server error is returned on most exceptions
(outside of debug mode) unauthorized / bad request / forbidden are sent
See also: REST interface documentation
and you can control any status code using HTTPStatusException, however it is treated as error and will result in a predefined error json which has statusMessage as exception message set and returns the HTTP status code you pass to it. (this is probably what you want for error handling)
You can also change what the errors look like by setting the errorHandler to a RestErrorHandler delegate in your RestInterfaceSettings.
Alternatively, depending on what you want to do, you can use a WebInterface which is a lot like a rest interface, but doesn't have some convenience functions REST interfaces do, but instead can fully access the request/response arguments and can basically do anything like a normal http route and has some other convenience functions you can use.
In theory you could abuse the errorHandler + HTTPStatusException with valid HTTP status codes if you want to return custom success codes with your data, but I would discourage that and instead go with web interfaces if that's what you are after.
However if all you want to do is having custom error codes with a custom, but consistent, error page then I would definitely go with REST interface with an errorHandler.
Your could could now look like this:
import vibe.vibe;
import std.uni;
#safe:
void main() {
auto server = new HTTPServerSettings;
server.port = 3000;
server.bindAddresses = ["::1", "127.0.0.1"];
auto router = new URLRouter;
RestInterfaceSettings settings = new RestInterfaceSettings();
// this is how the error page will look on any thrown exception (like HTTPStatusException)
settings.errorHandler = (HTTPServerRequest req, HTTPServerResponse res,
RestErrorInformation error) #safe {
res.writeJsonBody([
// design this however you like
"ok": Json(false),
"error": serializeToJson([
"status": Json(cast(int)error.statusCode),
"message": Json(error.exception.msg),
"parent": Json("/api/something")
])
]);
};
router.registerRestInterface(new Impl, settings);
listenHTTP(server, router);
runApplication();
}
interface RestAPI {
string getGreeting(string name);
}
class Impl : RestAPI {
string getGreeting(string name)
{
// throw an HTTP Bad Request error when name is empty
if (name.length == 0)
throw new HTTPStatusException(HTTPStatus.badRequest, "Name parameter cannot be empty!");
// throw an HTTP Conflict error code when name is Bob
if (sicmp(name, "bob") == 0)
throw new HTTPStatusException(HTTPStatus.conflict, "Server cannot greet Bob!");
return "Hello, " ~ name ~ "!";
}
}
and your server will then respond something like:
{
"ok": false,
"error": {
"message": "Server cannot greet Bob!",
"status": 409,
"parent": "/api/something"
}
}

You can try hunt framework, sample code for Rest api:
module app.controller.myapi;
import hunt.framework;
import app.message.UserMessage;
class MyapiController : Controller
{
mixin MakeController;
#Action
JsonResponse test()
{
UserMessage user;
user.id = 1;
user.name = "MyName";
user.email = "test#domain.com";
return new JsonResponse(user);
}
}
Your response struct:
module app.message.ResultMessage;
struct UserMessage
{
int id;
string name;
string email;
}
Response result is:
[ "id": 1, "name": "MyName", "email": "test#domain.com" ]

Related

How to write Unit Test for HTTPBuilder using SPOCK framework?

I want the Unit Test to go through both, Success and Failure execution paths. How to make the test case to go to Success or Failure path?
void addRespondents()
{
http.request(POST, TEXT) {
uri.path = PATH
headers.Cookie = novaAuthentication
headers.Accept = 'application/json'
headers.ContentType = 'application/json'
body = respondentString
response.success = { resp, json ->
statusCode = 2
}
response.failure = { resp, json ->
if(resp.status == 400) {
statusCode = 3
def parsedJson = new JsonSlurper().parse(json)
}else{
autoCreditResponse = createErrorResponse(resp)
}
}
}
}
OK, it seems you use this library:
<dependency>
<groupId>org.codehaus.groovy.modules.http-builder</groupId>
<artifactId>http-builder</artifactId>
<version>0.7.1</version>
</dependency>
Because I never used HTTPBuilder before and it looks like a nice tool when using Groovy, I played around with it a bit, replicating your use case, but converting it into a full MCVE. I have to admit that testability for this library is in bad shape. Even the tests for the library itself are no proper unit tests, but rather integration tests, actually performing network requests instead of mocking them. The tool itself also contains to test mocks or hints about how to test.
Because the functionality heavily relies on dynamically binding variables in closures, mock-testing is somewhat ugly and I had to look into the tool's source code in order to pull it off. Nice black-box testing is basically impossible, but here is how you can inject a mock HTTP client returning a predefined mock response which contains enough information not to derail the application code:
Class under test
As you can see, I added enough data in the class to be able to run it and do something meaningful. The fact that your method returns void instead of a testable result and we only have to rely on testing side effects, does not make testing easier.
package de.scrum_master.stackoverflow.q68093910
import groovy.json.JsonSlurper
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseDecorator
import static groovyx.net.http.ContentType.TEXT
import static groovyx.net.http.Method.POST
class JsonApiClient {
HTTPBuilder http = new HTTPBuilder("https://jsonplaceholder.typicode.com")
String PATH = "/users"
String novaAuthentication = ''
String respondentString = ''
String autoCreditResponse = ''
int statusCode
JsonSlurper jsonSlurper = new JsonSlurper()
void addRespondents() {
http.request(POST, TEXT) {
uri.path = PATH
headers.Cookie = novaAuthentication
headers.Accept = 'application/json'
headers.ContentType = 'application/json'
body = respondentString
response.success = { resp, json ->
println "Success -> ${jsonSlurper.parse(json)}"
statusCode = 2
}
response.failure = { resp, json ->
if (resp.status == 400) {
println "Error 400 -> ${jsonSlurper.parse(json)}"
statusCode = 3
}
else {
println "Other error -> ${jsonSlurper.parse(json)}"
autoCreditResponse = createErrorResponse(resp)
}
}
}
}
String createErrorResponse(HttpResponseDecorator responseDecorator) {
"ERROR"
}
}
Spock specification
This spec covers all 3 cases for responses in the above code, using an unrolled test which returns different status codes.
Because the method under test returns void, I decided to verify the side effect that HTTPBuilder.request was actually called. In order to do this, I had to use a Spy on the HTTPBuilder. Testing for this side effect is optional, then you do not need the spy.
package de.scrum_master.stackoverflow.q68093910
import groovyx.net.http.HTTPBuilder
import org.apache.http.HttpResponse
import org.apache.http.client.HttpClient
import org.apache.http.client.ResponseHandler
import org.apache.http.entity.StringEntity
import org.apache.http.message.BasicHttpResponse
import org.apache.http.message.BasicStatusLine
import spock.lang.Specification
import spock.lang.Unroll
import static groovyx.net.http.ContentType.TEXT
import static groovyx.net.http.Method.POST
import static org.apache.http.HttpVersion.HTTP_1_1
class JsonApiClientTest extends Specification {
#Unroll
def "verify status code #statusCode"() {
given: "a JSON response"
HttpResponse response = new BasicHttpResponse(
new BasicStatusLine(HTTP_1_1, statusCode, "my reason")
)
def json = "{ \"name\" : \"JSON-$statusCode\" }"
response.setEntity(new StringEntity(json))
and: "a mock HTTP client returning the JSON response"
HttpClient httpClient = Mock() {
execute(_, _ as ResponseHandler, _) >> { List args ->
(args[1] as ResponseHandler).handleResponse(response)
}
}
and: "an HTTP builder spy using the mock HTTP client"
HTTPBuilder httpBuilder = Spy(constructorArgs: ["https://foo.bar"])
httpBuilder.setClient(httpClient)
and: "a JSON API client using the HTTP builder spy"
def builderUser = new JsonApiClient(http: httpBuilder)
when: "calling 'addRespondents'"
builderUser.addRespondents()
then: "'HTTPBuilder.request' was called as expected"
1 * httpBuilder.request(POST, TEXT, _)
where:
statusCode << [200, 400, 404]
}
}
If you have used Spock for a while, probably I do not need to explain much. If you are a Spock or mock testing beginner, probably this is a bit too complex. But FWIW, I hope that if you study the code, you can wrap your head around how I did it. I tried to use Spock label comments in order to explain it.
Console log
The console log indicates that all 3 execution paths are covered by the specification:
Success -> [name:JSON-200]
Error 400 -> [name:JSON-400]
Other error -> [name:JSON-404]
If you use a code coverage tool, of course you do not need the log statements I inserted into the application code. They are just for demonstration purposes.
Verifying the result of http.request(POST, TEXT) {...}
In order to circumvent the fact that your method returns void, you can save the result of HTTPBuilder.request(..) by stubbing the method call in the spy interaction, passing through the original result at first, but also checking for the expected result.
Simply add def actualResult somewhere in the given ... and blocks (in when it is too late), then assign the result of callRealMethod() to it and then compare to expectedResult like this:
and: "a JSON API client using the HTTP builder spy"
def builderUser = new JsonApiClient(http: httpBuilder)
def actualResult
when: "calling 'addRespondents'"
builderUser.addRespondents()
then: "'HTTPBuilder.request' was called as expected"
1 * httpBuilder.request(POST, TEXT, _) >> {
actualResult = callRealMethod()
}
actualResult == expectedResult
where:
statusCode << [200, 400, 404]
expectedResult << [2, 3, "ERROR"]
If you prefer a data table instead of data pipes, the where block looks like this:
where:
statusCode | expectedResult
200 | 2
400 | 3
404 | "ERROR"
I think this pretty much covers all that makes sense to test here.

Bug with sending incorrect json doc

I am testing example-crud for applying your framework in my project. But I noticed some bug.
I try to get value from the sent json into std::string with this way:
std::string test = userDto->userName->c_str();
ENDPOINT("POST", "users", createUser, BODY_DTO(Object<UserDto>, userDto))
{
std::string test = userDto->userName->c_str();
return createDtoResponse(Status::CODE_200, m_userService.createUser(userDto));
}
UserDto defined like this:
class UserDto : public oatpp::DTO {
DTO_INIT(UserDto, DTO)
DTO_FIELD(Int32, id);
DTO_FIELD(String, userName, "username");
};
If i send correct json with post query, i get value to "test" variable properly.
{
"id": 0,
"username": "test_user"
}
But if i change json`s field "username" to, as example, "Username" and send this json to endpoin, server will faild with error "Segmentation fault".
Server must'n faild under such condition. It must giv error message to the sender, and continue run.
How can i avoid the failure, if i just neen to get one simple value to variable?
Since the DTO field name and the JSON-key doesn't match, the userName of the received DTO is null. So the solution would be to add a nullptr check:
ENDPOINT("POST", "users", createUser,
BODY_DTO(Object<UserDto>, userDto))
{
OATPP_ASSERT_HTTP(userDto->userName, Status::CODE_400, "username is required!"); // check field for nullptr
std::string test = userDto->userName->std_str();
...
return createDtoResponse(Status::CODE_200, m_userService.createUser(userDto));
}
Source: https://github.com/oatpp/oatpp/issues/340#issuecomment-727563144

How to access request headers in loopback4?

I used a simple statement like below to access headers in loopback4.
console.log(request.headers);
But it is printing undefined. A sample request headers that I want to access is in the image.
request header image
I am receiving the request and its headers which is perfectly fine. It's just that I am not able to access its headers as I am getting undefined from request.headers.
I am a beginner in loopback so pls explain it.
If i have to use a bodyparser then how i would have to use it in loopback4 because it is different from express.
Update
The original answer, although valid, is not the recommended way. Use dependency injection instead:
import {inject} from '#loopback/core';
import {get, param} from '#loopback/rest';
export class SomethingController {
constructor() {}
#get('/something')
something(#param.header.string('x-your-header') yourHeader: string): void {
// Use your header.
// e.g. Log to console
console.log(yourHeader);
}
}
Unlike the REQUEST object, this strips away unnecessary info and provides built-in coercion.
Further reading
https://loopback.io/doc/en/lb4/Decorators_openapi.html#parameter-decorator
https://loopback.io/doc/en/lb4/apidocs.openapi-v3.param.html
Original answer
If you're attempting to access the headers in a Controller, then you can inject the REQUEST object:
import {inject} from '#loopback/core';
import {get, Request, RestBindings} from '#loopback/rest';
export class SomethingController {
constructor(#inject(RestBindings.Http.REQUEST) private req: Request) {}
#get('/something')
something(): void {
// Get the headers
this.req.headers;
}
}
You can also use Context, example
import {inject} from '#loopback/core';
import {
post, RequestContext
} from '#loopback/rest';
export class UserController {
constructor(
#inject.context()
public context: RequestContext,
) {}
#post('/users/logout', {
responses: {
'200': {
description: 'Return success',
content: {
'application/json': {
schema: {
type: 'object'
},
},
},
},
},
})
async logout(): Promise<object> {
// ensure the token exists
const authHeader = this.context.request.headers.authorization;
if(authHeader && authHeader.split(" ")[1]){
// remove token
return {code:204, status: true, message:'Logout successful'};
}else{
return {code: 404, status: false, message:'Something went wrong'};
}
}
}
In sequence.ts, request object is called via context.
const {request, response} = context;
console.log(request.headers)
add log in sequence.ts to get the request headers.

how to get the error response from MoyaError

similar to this but this time i need to retrieve the JSOn response of the server.
here is my existing code:
return Observable.create{ observer in
let _ = self.provider
.request(.getMerchantDetails(qrId: qrId))
.filterSuccessfulStatusCodes()
.mapJSON()
.subscribe(onNext: { response in
observer.onNext(RQRMerchant(json: JSON(response)))
}, onError: { error in
observer.onError(error)
})
return Disposables.create()
my question is: I can get the error response code 404 by error.localizedDescription But I also want to get the JSON response of the 404 HTTP request.
I've been faced with the same problem, and for me the easiest and cleanest solution was to extend MoyaError to include a property for the decoded error object. In my case I'm using Decodable objects, so you could write something like this for a decodable BackendError representing the error you may get from your server:
extension MoyaError {
public var backendError: BackendError? {
return response.flatMap {
try? $0.map(BackendError.self)
}
}
}
If you instead prefer to directly deal with JSON you can invoke the mapJSONmethod instead of mapping to a Decodable.
Then you just have to do the following to get the error information for non successful status codes:
onError: { error in
let backendError = (error as? MoyaError).backendError
}
Since the response of your server is also contained in a JSON, that means that your onNext emissions can be successful JSON responses or invalid JSON responses.
Check the validity of the response using do operator
You can check for the validity of the response by doing the following:
return Observable.create{ observer in
let _ = self.provider
.request(.getMerchantDetails(qrId: qrId))
.filterSuccessfulStatusCodes()
.mapJSON()
.do(onNext: { response in
let isValidResponse : Bool = false // check if response is valid
if !isValidResponse {
throw CustomError.reason
}
})
.subscribe(onNext: { response in
observer.onNext(RQRMerchant(json: JSON(response)))
}, onError: { error in
observer.onError(error)
})
return Disposables.create()
Use the do operator
Check if the onNext emission is indeed a valid emission
Throw an error if it is invalid, signifying that the observable operation has failed.
Response validation
To keep your response validation code in the right place, you can define a class function within your response class definition that verifies if it is valid or not:
class ResponseOfTypeA {
public class func isValid(response: ResponseOfTypeA) throws {
if errorConditionIsTrue {
throw CustomError.reason
}
}
}
So that you can do the following:
// Your observable sequence
.mapJSON()
.do(onNext: ResponseOfTypeA.isValid)
.subscribe(onNext: { response in
// the rest of your code
})

Retrofit 2.1 Internal Server Error with Post

I am using Retrofit 2.1 and when i am posting an object to my server, it gives me Internal server error with status code = 500, but i try to to post from my backend, it works like a charm, I am sure this is not server's problem.
Undoubtedly, i should use retrofit as a singleton:
//return api if not null
HereApi getApi(){
if (api == null) {
api = getRetrofit().create(HereApi.class);
}
return api;
}
//returns restadapter if not null
Retrofit getRetrofit(){
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl("my endpoint")
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
and this method that i post Here object:
void createHere(Here here){
List<Here> list = new ArrayList<>();
list.add(here);
Call<List<Here>> call = getApi().createHere(list);
call.enqueue(new Callback<List<Here>>() {
#Override
public void onResponse(Call<List<Here>> call, Response<List<Here>> response) {
Log.i(TAG, "onResponse: "+response.message());
}
#Override
public void onFailure(Call<List<Here>> call, Throwable t) {
}
});
}
I tried to post a list with single object inside and to post one object alone, but still status code is 500 ;*(
This is my api service interface:
public interface HereApi{
#GET("/lessons/")
Call<List<Lesson>> getLesson(#QueryMap Map<String,String> map);
#Headers({
"Content-Type: application/json",
"Vary: Accept"
})
#POST("/heres/")
Call<List<Here>> createHere(#Body List<Here> list);
#GET("/heres/")
Call<List<Here>> getHeres(#QueryMap Map<String,String> map);
}
I have written backend in Django + Django-rest-framework:
When I try to post from this, it just works:
I need your help guys, i have only one day to complete this project!!!
Hi I think there is a datetime conversation issue.
Use Jackson formating attonation in order to properly serialize datetime field.