Where is the best way to log response in Restlet - restlet

I use Restlet with Jetty8.
The Jetty log all incoming calls.
I want to log all response data also, url and body.
Where is the best place to put the log code?
I thought createOutboundRoot is the place but I didn't figured out how to use it and couldn’t find any examples in the web.

I have never tried it but I would start with a Custom filter and override After handle, this appears to be the way Restlet itself does logging internally see the class LogFilter.

implements a log filter like this :
public class CustomLogFilter extends Filter {
public CustomLogFilter() {
super();
}
protected int beforeHandle(Request request, Response response) {
int returned = super.beforeHandle(request, response);
// Do specific log if needed
return returned;
}
protected void afterHandle(Request request, Response response) {
super.afterHandle(request, response);
// Do specific log if needed
}
}
and use it in your createInboundRoot if you have an Application object :
public synchronized Restlet createInboundRoot() {
final Router router = new Router(getContext());
CustomLogFilter filter = new CustomLogFilter();
filter.setNext(router);
return filter;
}

Related

Modify response using middleware in ASP.NET Core 3

My goal is to write a middleware that will take care of logging requests to my API and API's responses to those requests in a DB.
I already made a middleware that handles exceptions in a similar fashion, but I got stumped over this.
When you read MSDN about Middleware you can see this nice picture:
This makes you think that Middleware 2 receives the requests, does certain manipulations with it and passes it onto Middleware 3, then once all processing is done by middleware 3 it passes controls back to Middleware 2 for additional processing.
The only thing I do not understand is how to log the response if Middleware 2 Invoke() method is only called once during the request and not called during the response?
Startup.cs:
app.UseMiddleware<RequestLoggingMiddleware>();
Middleware:
public class RequestLoggingMiddleware
{
private readonly RequestDelegate nextMiddleware;
public RequestLoggingMiddleware(RequestDelegate nextMiddleware)
{
this.nextMiddleware = nextMiddleware;
this.options = options;
}
public async Task Invoke(HttpContext context)
{
System.Diagnostics.Debug.WriteLine("Middleware runs");
await nextMiddleware(context);
}
}
}
In the example above I only see "Middleware runs" once in a console, during the initial request but before the response is made. How do I get it to run during the response cycle?
To get the response, all you need to do is apply your same logic after the await nextMiddleware(context); line.
For example, to log the status code:
public class RequestLoggingMiddleware
{
private readonly RequestDelegate nextMiddleware;
public RequestLoggingMiddleware(RequestDelegate nextMiddleware)
{
this.nextMiddleware = nextMiddleware;
}
public async Task Invoke(HttpContext context)
{
System.Diagnostics.Debug.WriteLine("Middleware runs");
await nextMiddleware(context);
System.Diagnostics.Debug.WriteLine($"Response Code: {context.Response.StatusCode}");
}
}

Transferring objects across service provider scopes

I have a CorrelationIdMiddleware that is inspecting incoming request headers and setting a scoped CorrelationId later propagated to all HttpClients.
public class CorrelationId {
public string Value { get;set; }
}
public void ConfigureServices(IServiceCollection services) {
...
services.AddScoped<CorrelationId>();
...
}
I have run into a use case where I need to create an isolated scope around a section of code, but would like the CorrelationId from the scope of the http request to propagate into the isolated scope (The isolated scope has an HttpClient which I would like to have the same header attached).
I would like to spawn off a background Task that is created from DI w/ any required dependencies and for any HttpClients to have headers injected via HttpClientFactory plugins.
public Controller {
public Controller(IServiceProvider serviceProvider, CorrelationId correlationId) { ... }
public IActionResult PostTask() {
var isolatedScope = _serviceProvider.CreateScope();
var action = () => {
using(isolatedScope) {
var backgroundJob = isolatedScope
.ServiceProvider
.GetRequiredService<IBackgroundJob>();
backgroundJob.Execute();
// scopedCorrelationId =/= correlationId
// how can i get correlationId to jump scopes?
}
};
return Task.Factory.StartNew(
action,
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
}
Is there a way to transfer certain objects into the isolated scope? Ideally without having to know the typeof(object) i need to transfer.
It is not possible to transfer objects between the parent scope and the isolated scope.
Information like the CorrelationId arriving with the headers of a request better fits with the HttpContext or with an AsyncLocal variable if it needs to be propagated through an async execution flow.

Custom error code pages with message

I am trying to create a custom error code page that displays a message I pass to it in my .NET Core MVC 1.1 application. I setup custom error code pages support in the Startup.cs class file and then created a simple view in a controller that does public IActionResult Example1 => NotFound("Some custom error message"). I expected this message to be pushed to the controller however this is not the case. Calling NotFound() without any parameters hits the error controller but as soon as I pass a message through, the controller is never used and a simple text message is displayed.
I could have sworn I used to do this in the past with classic .NET MVC but it has been awhile.
How can I have custom error code pages that display the proper error. I also need the ability in a controller to return the standard text or JSON response during the error for cases when I expect a JSON response (API actions and such). I am assuming there is a way to do this with a attribute but I have yet to find a way to do either of these tasks.
What you could do is something similar to how the StatusCodePages middleware works. That middleware allows a pipeline re-execution model, to allow handling status code errors through the normal MVC pipeline. So when you return a non-successful status code from MVC, the middleware detects that and then re-executes the whole pipeline for a status code error route. That way, you are able to fully design status code errors. But as Chris Pratt already mentioned, those status codes are typically limited to just their code. There is not really a way to add additional details to it.
But what we could do is create our own error handling implementation on top of that re-execution model. For that, we create a CustomErrorResponseMiddleware which basically checks for CustomErrorResponseException exceptions and then re-executes the middleware pipeline for our error handler.
// Custom exceptions that can be thrown within the middleware
public class CustomErrorResponseException : Exception
{
public int StatusCode { get; set; }
public CustomErrorResponseException(string message, int statusCode)
: base(message)
{
StatusCode = statusCode;
}
}
public class NotFoundResponseException : CustomErrorResponseException
{
public NotFoundResponseException(string message)
: base(message, 404)
{ }
}
// Custom context feature, to store information from the exception
public interface ICustomErrorResponseFeature
{
int StatusCode { get; set; }
string StatusMessage { get; set; }
}
public class CustomErrorResponseFeature : ICustomErrorResponseFeature
{
public int StatusCode { get; set; }
public string StatusMessage { get; set; }
}
// Middleware implementation
public class CustomErrorResponseMiddleware
{
private readonly RequestDelegate _next;
private readonly string _requestPath;
public CustomErrorResponseMiddleware(RequestDelegate next, string requestPath)
{
_next = next;
_requestPath = requestPath;
}
public async Task Invoke(HttpContext context)
{
try
{
// run the pipeline normally
await _next(context);
}
catch (CustomErrorResponseException ex)
{
// store error information to be retrieved in the custom handler
context.Features.Set<ICustomErrorResponseFeature>(new CustomErrorResponseFeature
{
StatusCode = ex.StatusCode,
StatusMessage = ex.Message,
});
// backup original request data
var originalPath = context.Request.Path;
var originalQueryString = context.Request.QueryString;
// set new request data for re-execution
context.Request.Path = _requestPath;
context.Request.QueryString = QueryString.Empty;
try
{
// re-execute middleware pipeline
await _next(context);
}
finally
{
// restore original request data
context.Request.Path = originalPath;
context.Request.QueryString = originalQueryString;
}
}
}
}
Now, all we need to do is hook that up. So we add the middleware within our Startup.Configure, somewhere near the beginning:
app.UseMiddleware<CustomErrorResponseMiddleware>("/custom-error-response");
The /custom-error-response is the route that we are re-executing when a custom response is being requested. This can be a normal MVC controller action:
[Route("/custom-error-response")]
public IActionResult CustomErrorResponse()
{
var customErrorResponseFeature = HttpContext.Features.Get<ICustomErrorResponseFeature>();
var view = View(customErrorResponseFeature);
view.StatusCode = customErrorResponseFeature.StatusCode;
return view;
}
Since this uses MVC, this also needs a view:
#model ICustomErrorResponseFeature
#{
ViewData["Title"] = "Error";
}
<p>There was an error with your request:</p>
<p>#Model.StatusMessage</p>
And that’s basically all. Now, we can just throw our custom error response exceptions from our MVC actions to trigger this:
// generate a 404
throw new NotFoundResponseException("This item could not be found");
// or completely custom
throw new CustomErrorResponseException("This did not work", 400);
Of course, we could also expand this further, but that should be the basic idea.
If you are already using the StatusCodePages middleware, you might think whether all this custom re-execution is really necessary, when you already have exactly that in the StatusCodePages middleware. And well, it is not. We can also just expand on that directly.
For that, we will just add the context features, which we can set at any point during the normal execution. Then, we just return a status code, and let the StatusCodePages middleware run. Inside its handler, we can then look for our feature and use the information there to expand the status code error page:
// Custom context feature
public interface IStatusCodePagesInfoFeature
{
string StatusMessage { get; set; }
}
public class StatusCodePagesInfoFeature : IStatusCodePagesInfoFeature
{
public string StatusMessage { get; set; }
}
// registration of the StatusCodePages middleware inside Startup.Configure
app.UseStatusCodePagesWithReExecute("/error/{0}");
// and the MVC action for that URL
[Route("/error/{code}")]
public IActionResult StatusCode(int code)
{
var statusCodePagesInfoFeature = HttpContext.Features.Get<IStatusCodePagesInfoFeature>();
return View(model: statusCodePagesInfoFeature?.StatusMessage);
}
Inside of the normal controller actions, we can set that feature before returning a status code:
HttpContext.Features.Set<IStatusCodePagesInfoFeature>(new StatusCodePagesInfoFeature
{
StatusMessage = "This item could not be found"
});
return NotFound();
It is too bad you cannot intercept NotFound, Unauthorized, etc. responses in a middleware class.
Okay, option three! You can totally intercept those responses, just not inside of middleware, since these are MVC results and will not leave the MVC pipeline. So you have to intercept them within the MVC filter pipeline. But we could absolutely run a filter, for example a result filter, that modifies the result.
The problem is that we still need a way to pass the information on. We could use a context feature again, but we can also use the MVC object results. So the idea is that we can just do the following in the MVC actions:
return NotFound("The item was not found");
So usually, that string would be the plain text response. But before the result is being executed and the response is being generated, we can run a result filter to modify this and return a view result instead.
public class StatusCodeResultFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
// retrieve a typed controller, so we can reuse its data
if (context.Controller is Controller controller)
{
// intercept the NotFoundObjectResult
if (context.Result is NotFoundObjectResult notFoundResult)
{
// set the model, or other view data
controller.ViewData.Model = notFoundResult.Value;
// replace the result by a view result
context.Result = new ViewResult()
{
StatusCode = 404,
ViewName = "Views/Errors/NotFound.cshtml",
ViewData = controller.ViewData,
TempData = controller.TempData,
};
}
// intercept other results here…
}
await next();
}
}
All you need is a view at Views/Errors/NotFound.cshtml now and everything will magically work once you have the filter registered.
You can either register the filter by adding a [TypeFilter(typeof(StatusCodeResultFilter))] attribute to the controller or individual actions, or you can register it globally.
What you want is not possible. When you do something like return NotFound with a message, that message will be included in the response body only if it's left unmolested. When you do something like enable status code pages, the NotFound is simply caught by the middleware, and the request will simply be handed off to your error handling action to ultimately obtain the response. Importantly, that means your original NotFoundResult along with any custom message has been round-filed.

Lightinject with Web API - How can I get the HttpRequestMessage?

How can I get the current HttpRequestMessage when using Lightinject with Web API?
In Simple Injector, for example, I have the following extension method on the container:
var msg = container.GetCurrentHttpRequestMessage()
But I couldn't find a matching one in Lightinject.
If you take a look in the Simple Injector Web API integration source code, you'll find that the implementation is actually really straightforward. You can easily implement this yourself.
What you need is to create a custom DelegatingHandler that stores the current HttpRequestMessage in a way that you can retrieve it later on, as follows:
public sealed class HttpRequestMessageDelegatingHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken) {
CurrentMessage = request;
return base.SendAsync(request, cancellationToken);
}
public static HttpRequestMessage CurrentMessage {
get { return (HttpRequestMessage)CallContext.LogicalGetData("RequestMessage"); }
private set { CallContext.LogicalSetData(value, "RequestMessage"); }
}
}
// Register this handler as follows in Web API
configuration.MessageHandlers.Add(new HttpRequestMessageDelegatingHandler());
Now you can retrieve the request's current message as follows:
HttpRequestMessageDelegatingHandler.CurrentMessage
The Simple Injector documentation advises to hide this call behind a custom abstraction, such as a simple IRequestMessageProvider. An implementation is of course easily created:
private sealed class RequestMessageProvider : IRequestMessageProvider {
public HttpRequestMessage CurrentMessage {
get { return HttpRequestMessageDelegatingHandler.CurrentMessage; }
}
}
Instead of creating an IRequestMessageProvider abstraction plus implementation, you can register it directly as delegate as follows:
container.Register<Func<HttpRequestMessage>>(_ =>
() => HttpRequestMessageDelegatingHandler.CurrentMessage);
My preference is to use an interface instead of a Func<T>, because such interface is much more explicit and readable.

Spring & Reslet : is it possible to map a URL path component to a method argument?

I'm new to Restlet, but I've followed the tutorial on Restlet's own website and got a basic application up and running. What I'm doing right now is that I'm setting up a basic ServerResource and expose a #Get method.
What I'd like is to be able to invoke /user/{userId} and get the user representation back. Is it possible, somehow, to hand over the mapping of {userId} to Restlet, which in turn would invoke getUser(String userId) in my ServerResource?
Such feature (binding path variables into annotated method parameters) isn't natively supported in the framework. Such mapping in the annotated method signatures is only supported with input representation.
To get the path variables of a request, you can get them from the request object (method getAttribute), as described below:
public class UserServerResource extends ServerResource {
#Get
public User getUser() {
String userId = getAttribute("userId");
User user = (...)
(...)
return user;
}
}
If you want to share this path variable across several methods, you can define it as a instance variable (notice that a new instance of the server resource is created for each request unlike to Spring REST where each controller is a singleton and such variable must be defined in method signatures). We can leverage the method doInit of the server resource, as described below:
public class UserServerResource extends ServerResource {
private String userId;
private User user;
#Override
protected void doInit() throws ResourceException {
super.doInit();
userId = getAttribute("userId");
// for example
user = loadUser(userId);
// throws a status 404 if user can't be found
setExisting(user != null);
}
#Get
public User getUser() {
return user;
}
#Put
public User saveUser(User user) {
saveUser(user);
return user;
}
#Delete
public void deleteUser() {
deleteUser(user);
}
}
If you really want to use a mapping from request elements (like path variables, ...) to method parameters, you should use JAXRS. Restlet provides a support of this specification. Implementing a similar server resource as above but with JAXRS is described below:
#Path("/users/{userId}")
public class UserResource {
#GET
#Produces("text/xml")
public String getUser(#PathParam("userId") String userId) {
(...)
}
}
For more details, you can have a look at the corresponding documentation: http://restlet.com/technical-resources/restlet-framework/guide/2.2/extensions/jaxrs.
Hop it helps,
Thierry