Is it possible to validate the POST body in Akka Http?
Case Class Validationseems to work only for get requests.
As an example:
case class User(name: String){
require(name)
}
.....
(post & entity(as[User])) { user =>
complete(doSomething(user.name))
}
I would like the above code to throw a ValidationRejection rejection
You can use require inside scope of complete directive
(path("stats") & parameter("idsParam")) { idsParam =>
complete {
require(idsParam.length > 1)
val ids = idsParam.split(",").map(v => CaseId(v).value)
DBManager.getArticleStats(ids).map { case (id, stats) => IdWithValue(CaseId(id), stats) }
}
}
that handles yours POST request.
And typically I have custom exception handler that wraps all exceptions into format that my API client expects such as json. require throws IllegalArgumentException so let's handle it in special way if we want to.
protected implicit def myExceptionHandler =
ExceptionHandler {
case ex: IllegalArgumentException => ctx => {
val cause = ex.getCause
ex.printStackTrace()
ctx.complete(StatusCodes.InternalServerError, ErrorResponse(ex.code, ex))
}
case ex: Throwable => ctx =>
logger.warning("Request {} could not be handled normally", ctx.request)
ex.printStackTrace()
ctx.complete(StatusCodes.InternalServerError, ErrorResponse(StatusCodes.InternalServerError.intValue, ex))
}
where ErrorResponse is my case class that is being serialized to json using spray-json
Related
For any 4xx or 5xx response given out by my micronaut server, I'd like to log the response status code and endpoint it targeted. It looks like a filter would be a good place for this, but I can't seem to figure out how to plug into the onError handling
for instance, this filter
#Filter("/**")
class RequestLoggerFilter: OncePerRequestHttpServerFilter() {
companion object {
private val log = LogManager.getLogger(RequestLoggerFilter::class.java)
}
override fun doFilterOnce(request: HttpRequest<*>, chain: ServerFilterChain): Publisher<MutableHttpResponse<*>>? {
return Publishers.then(chain.proceed(request), ResponseLogger(request))
}
class ResponseLogger(private val request: HttpRequest<*>): Consumer<MutableHttpResponse<*>> {
override fun accept(response: MutableHttpResponse<*>) {
log.info("Status: ${response.status.code} Endpoint: ${request.path}")
}
}
}
only logs on a successful response and not on 4xx or 5xx responses.
How would i get this to hook into the onError handling?
You could do the following. Create your own ApplicationException ( extends RuntimeException), there you could handle your application errors and in particular how they result into http error codes. You exception could hold the status code as well.
Example:
class BadRequestException extends ApplicationException {
public HttpStatus getStatus() {
return HttpStatus.BAD_REQUEST;
}
}
You could have multiple of this ExceptionHandler for different purposes.
#Slf4j
#Produces
#Singleton
#Requires(classes = {ApplicationException.class, ExceptionHandler.class})
public class ApplicationExceptionHandler implements ExceptionHandler<ApplicationException, HttpResponse> {
#Override
public HttpResponse handle(final HttpRequest request, final ApplicationException exception) {
log.error("Application exception message={}, cause={}", exception.getMessage(), exception.getCause());
final String message = exception.getMessage();
final String code = exception.getClass().getSimpleName();
final ErrorCode error = new ErrorCode(message, code);
log.info("Status: ${exception.getStatus())} Endpoint: ${request.path}")
return HttpResponse.status(exception.getStatus()).body(error);
}
}
If you are trying to handle Micronaut native exceptions like 400 (Bad Request) produced by ConstraintExceptionHandler you will need to Replace the beans to do that.
I've posted example here how to handle ConstraintExceptionHandler.
If you want to only handle responses itself you could use this mapping each response code (example on #Controller so not sure if it works elsewhere even with global flag:
#Error(status = HttpStatus.NOT_FOUND, global = true)
public HttpResponse notFound(HttpRequest request) {
<...>
}
Example from Micronaut documentation.
Below code I used for adding custom cors headers in the error responses, in doOnError you can log errors
#Filter("/**")
public class ResponseCORSAdder implements HttpServerFilter {
#Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
return this.trace(request)
.switchMap(aBoolean -> chain.proceed(request))
.doOnError(error -> {
if (error instanceof MutableHttpResponse<?>) {
MutableHttpResponse<?> res = (MutableHttpResponse<?>) error;
addCorsHeaders(res);
}
})
.doOnNext(res -> addCorsHeaders(res));
}
private MutableHttpResponse<?> addCorsHeaders(MutableHttpResponse<?> res) {
return res
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "OPTIONS,POST,GET")
.header("Access-Control-Allow-Credentials", "true");
}
private Flowable<Boolean> trace(HttpRequest<?> request) {
return Flowable.fromCallable(() -> {
// trace logic here, potentially performing I/O
return true;
}).subscribeOn(Schedulers.io());
}
}
I have a Rest Web Client todo an API call and I handle the exceptions as given below.
I want to handle 404, 401 and 400 errors in a global way rather than handling at the individual client level. How can we achieve the same.
public Mono<ProductResponse> getProductInformation(String productId) {
return webClient.get()
.uri("/v1/products/"+productId)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus( httpStatus -> HttpStatus.NOT_FOUND.equals(httpStatus), clientResponse -> {
Mono<NotFound> notFound = clientResponse.bodyToMono(NotFound.class);
return notFound.flatMap( msg -> {
log.info(" Error Message {}" , msg.getErrorMsg());
return Mono.error(new NotFoundException(msg.getErrorMsg()));
});
}).onStatus( httpStatus -> HttpStatus.UNAUTHORIZED.equals(httpStatus), clientResponse -> {
Mono<NotFound> notFound = clientResponse.bodyToMono(NotFound.class);
return Mono.error(new NotAuthorisedException("Unauthorised"));
}).bodyToMono(ProductResponse.class);
}
Two approaches:
Exceptions with webclients are all wrapped in WebClientResponseException class. You can handle that using Spring's ExceptionHandler annotation like this.
#ExceptionHandler(WebClientResponseException.class)
public ResponseEntity handleWebClientException(WebClientResponseException ex){
return ResponseEntity.badRequest().body(ex.getResponseBodyAsString());
}
Note - Here you can write complex conditional logic based on the response status, by using methods like getStatusCode(), getRawStatusCode(), getStatusText(), getHeaders() and getResponseBodyAsString(). Also you can get reference of the request that was sent using the method getRequest.
Using ExchangeFilterFunction while constructing the webclient bean.
#Bean
public WebClient buildWebClient() {
Function<ClientResponse, Mono<ClientResponse>> webclientResponseProcessor =
clientResponse -> {
HttpStatus responseStatus = clientResponse.statusCode();
if (responseStatus.is4xxClientError()) {
System.out.println("4xx error");
return Mono.error(new MyCustomClientException());
} else if (responseStatus.is5xxServerError()) {
System.out.println("5xx error");
return Mono.error(new MyCustomClientException());
}
return Mono.just(clientResponse);
};
return WebClient.builder()
.filter(ExchangeFilterFunction.ofResponseProcessor(webclientResponseProcessor)).build();
}
Then you can either handle the MyCustomClientException using #ExceptionHandler or leave it as it is.
I create a rest API in express, and I want to create a helper to make every response have the same object.
I have a following code in typescript
abstract class BaseService {
...
protected sendOK(res: Response, msg?: string | object): object {
res.status(200);
return res.send({
status: "success",
message: msg
})
}
...
}
class Device extends BaseService {
...
public create = async (req: Request, res: Response) => {
...
return this.sendOK(res, msg)
}
...
}
the code above is working, but what I'm asking is there any way to call this.sendOK(msg) without passing the res object?
On an ASP.NET Core I have the following controller:
public class MessageApiController : Controller {
private readonly IMediator _mediator;
public MessageApiController(IMediator mediator) {
_mediator = mediator;
}
[HttpGet("messages")]
public async Task<IActionResult> Get(MessageGetQuery query) {
MessageGetReply reply = await _mediator.SendAsync(query);
return Ok(reply);
}
[HttpDelete("messages")]
public async Task<IActionResult> Delete(MessageDeleteModel model) {
MessageDeleteReply reply = await _mediator.SendAsync(model);
return Ok(reply);
}
}
I have handlers classes with a method handle to perform this actions:
GET (short code for sake of simplicity)
public async MessageGetReply Handle(MessageGetQuery query) {
IQueryable<Message> messages = _context.Messages.AsQueryable();
messages = messages.Include(x => x.Author).Include(x => x.Recipients);
// Omitted: Filter messages according to query
List<Message> result = await messages.ToListAsync();
// Omitted: Create MessageGetReply from result
} // Handle
DELETE (short code for sake of simplicity)
public async MessageDeleteReply Handle(MessageDeleteModel model) {
Message message = await _context.Messages.FirstOrDefaultAsync(x => x.Id == model.Id);
if (message != null) {
_context.Remove(message);
await _context.SaveChangesAsync();
}
// Omitted: Return reply
} // Handle
The authorization scenario is the following:
GET
1. The user must be authenticated
2. User.Id must equal Message.RecipientId;
DELETE
1. The user must be authenticated
2. User.Id must equal Message.AuthorId;
So I created the following resource authorization handler:
public class MessageAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Message> {
protected override void Handle(AuthorizationContext context, OperationAuthorizationRequirement requirement, Message resource) {
if (requirement == Operations.Delete) {
if (resource.AuthorId.ToString() == context.User.FindFirstValue(ClaimTypes.NameIdentifier))
context.Succeed(requirement);
}
if (requirement == Operations.Read) {
if (resource.RecipientId.ToString() == context.User.FindFirstValue(ClaimTypes.NameIdentifier)))
context.Succeed(requirement);
}
} // Handle
}
There are a few problems that arise:
In GET should I pass all messages to Authorization Handler?
In fact the messages are filtered by MessageGetQuery.AuthorId ...
So the authorization handler could receive MessageGetQuery.AuthorId and not a List ...
But feels strange since the resource is the List of Messages.
MessageGetQuery is simply a DTO.
Authorization could be coupled to DTOs (Query and Model) but does it make sense?
The problem arises when the DTO has less information than the Entity and I need that information to take decisions on authorization ...
If using the entities as resources I loose the ability to do projection:
IQueryable<Message> messages = _context.Messages.AsQueryable();
messages = messages.Include(x => x.Author).Include(x => x.Recipients);
// Omitted: Filter messages according to query
List<Message> result = await messages.ToListAsync();
// CALL authorization and send the resource messages ...
// Omitted: Create MessageGetReply from result
One solution would be to have a AuthorizationHandler for Read Messages, one AuthorizationHandler for Delete message ... The first one would take a list of messages, the second one message, ... I would however have to many classes.
Everything becomes simpler when using Entities directly in controllers and no DTOs but that, IMHO, should not be done ...
In RC2 we removed the object restriction in auth handlers, so you'll be able to use, for example, ints. So you could inject your repo into a handler and pull out your DTOs as you wish.
I am working on a project which includes a REST API component. I have a controller dedicated to handling all of the REST API calls.
Is there any way to catch all exceptions for that specific controller so that I can take a different action for those exceptions than the rest of the application's controllers?
IE: I'd like to respond with either an XML/JSON formatted API response that contains the exception message, rather than the default system view/stack trace (which isn't really useful in an API context). Would prefer not having to wrap every method call in the controller in its own try/catch.
Thanks for any advice in advance.
You can completely bypass Yii's default error displaying mechanism by registering onError and onException event listeners.
Example:
class ApiController extends CController
{
public function init()
{
parent::init();
Yii::app()->attachEventHandler('onError',array($this,'handleError'));
Yii::app()->attachEventHandler('onException',array($this,'handleError'));
}
public function handleError(CEvent $event)
{
if ($event instanceof CExceptionEvent)
{
// handle exception
// ...
}
elseif($event instanceof CErrorEvent)
{
// handle error
// ...
}
$event->handled = TRUE;
}
// ...
}
I wasn't able to attach events in controller, and I did it by redefinition CWebApplication class:
class WebApplication extends CWebApplication
{
protected function init()
{
parent::init();
Yii::app()->attachEventHandler('onError',array($this, 'handleApiError'));
Yii::app()->attachEventHandler('onException',array($this, 'handleApiError'));
}
/**
* Error handler
* #param CEvent $event
*/
public function handleApiError(CEvent $event)
{
$statusCode = 500;
if($event instanceof CExceptionEvent)
{
$statusCode = $event->exception->statusCode;
$body = array(
'code' => $event->exception->getCode(),
'message' => $event->exception->getMessage(),
'file' => YII_DEBUG ? $event->exception->getFile() : '*',
'line' => YII_DEBUG ? $event->exception->getLine() : '*'
);
}
else
{
$body = array(
'code' => $event->code,
'message' => $event->message,
'file' => YII_DEBUG ? $event->file : '*',
'line' => YII_DEBUG ? $event->line : '*'
);
}
$event->handled = true;
ApiHelper::instance()->sendResponse($statusCode, $body);
}
}
In index.php:
require_once(dirname(__FILE__) . '/protected/components/WebApplication.php');
Yii::createApplication('WebApplication', $config)->run();
You can write your own actionError() function per controller. There are several ways of doing that described here
I'm using the following Base controller for an API, it's not stateless API, mind you, but it can serve just aswell.
class BaseJSONController extends CController{
public $data = array();
public $layout;
public function filters()
{
return array('mainLoop');
}
/**
* it all starts here
* #param unknown_type $filterChain
*/
public function filterMainLoop($filterChain){
$this->data['Success'] = true;
$this->data['ReturnMessage'] = "";
$this->data['ReturnCode'] = 0;
try{
$filterChain->run();
}catch (Exception $e){
$this->data['Success'] = false;
$this->data['ReturnMessage'] = $e->getMessage();
$this->data['ReturnCode'] = $e->getCode();
}
echo json_encode($this->data);
}
}
You could also catch dbException and email those, as they're somewhat critical and can show underlying problem in the code/db design.
Add this to your controller:
Yii::app()->setComponents(array(
'errorHandler'=>array(
'errorAction'=>'error/error'
)
));