My scenario is this:
A client application executes a HTTP POST against an endpoint exposed by OpenRasta.
The body of the request contains an error that causes a problem in the codec - which is a custom implementation of OpenRasta.Codecs.IMediaTypeReader. This converts a JSON payload to the POCO expected by the handler.
The codec throws an exception that describes the error in a useful way. For example: Newtonsoft.Json.JsonReaderException: After parsing a value an unexpected character was encountered: ". Line 4, position 5.
The client application receives a HTTP 405 - MethodNotAllowed. The client doesn't see any of the exception details.
If the codec is modified to catch a JsonReaderException and return Missing.Value, similar to the Implementing a codec wiki, then the client receives a HTTP 500 - Internal Server Error. The body of the response also describes the following exception:
System.InvalidOperationException: The operation is not ready for invocation.
at OpenRasta.OperationModel.MethodBased.MethodBasedOperation.Invoke()
at OpenRasta.OperationModel.Interceptors.OperationWithInterceptors.<Invoke>b__0()
at OpenRasta.OperationModel.Interceptors.OperationWithInterceptors.Invoke()
at OpenRasta.OperationModel.OperationExecutor.Execute(IEnumerable`1 operations)
at OpenRasta.Pipeline.Contributors.OperationInvokerContributor.ExecuteOperations(ICommunicationContext context)
at OpenRasta.Pipeline.PipelineRunner.ExecuteContributor(ICommunicationContext context, ContributorCall call)
How should I modify my application so that:
The client receives a HTTP 400 Bad Request.
The client receives a string containing the details of the exception encountered in the codec.
Here is a minor variation of the answer above - this time with codec selection based on the operation result data.
Within IConfigurationSource:
using (OpenRastaConfiguration.Manual)
{
ResourceSpace.Uses.PipelineContributor<ErrorCheckingContributor>();
ResourceSpace.Has.ResourcesOfType<ApplicationError>()
.WithoutUri
.TranscodedBy<ApplicationErrorCodec>();
// Or use a generic JSON serializer like this:
// .AsJsonDataContract();
// Other configuration here
}
Now ErrorCheckingContributor looks like this:
public class ErrorCheckingContributor : IPipelineContributor
{
public void Initialize(IPipeline pipelineRunner)
{
pipelineRunner
.Notify(CheckRequestDecoding)
.After<KnownStages.IOperationResultInvocation>()
.And.Before<KnownStages.ICodecResponseSelection>();
}
private static PipelineContinuation CheckRequestDecoding(ICommunicationContext context)
{
if (context.ServerErrors.Count == 0)
{
return PipelineContinuation.Continue;
}
Error err = context.ServerErrors[0];
// Get a suitable message (err.Message contains stack traces, so try to avoid that)
string msg = err.Title;
if (msg == null && err.Exception != null)
msg = err.Exception.Message;
if (msg == null)
msg = err.Message;
// Create instance of an error information resource which is specific for the application
// - This one is rather simple and only contains a copy of the message
ApplicationError error = new ApplicationError(msg);
// Set operation result to be "400 Bad Request" and remove errors
context.OperationResult = new OperationResult.BadRequest { ResponseResource = error };
context.ServerErrors.Clear();
// Render immediately without starting any handlers
return PipelineContinuation.RenderNow;
}
}
The class ApplicationError is:
public class ApplicationError
{
public string Message { get; set; }
public ApplicationError(string message)
{
Message = message;
}
}
At last we need a codec ApplicationErrorCodec for ApplicationError. This is not different from any other IMediaTypeWriter codec but depends a lot on your expected response media type. See https://github.com/openrasta/openrasta/wiki/Implementing-a-Codec for one example.
Having found this thread on Google Groups which contains all the answers, my current implementation looks something like this.
Within my implementation of IConfigurationSource:
using (OpenRastaConfiguration.Manual)
{
ResourceSpace.Uses.PipelineContributor<ErrorCheckingContributor>();
// Other configuration here
}
Then ErrorCheckingContributor looks something like this:
public class ErrorCheckingContributor : IPipelineContributor
{
public void Initialize(IPipeline pipelineRunner)
{
pipelineRunner
.Notify(CheckRequestDecoding)
.After<KnownStages.IOperationResultInvocation>()
.And.Before<KnownStages.ICodecResponseSelection>();
}
private static PipelineContinuation CheckRequestDecoding(ICommunicationContext context)
{
if (context.ServerErrors.Count == 0)
{
return PipelineContinuation.Continue;
}
var first = context.ServerErrors[0];
if (first.Exception is Newtonsoft.Json.JsonReaderException)
{
context.Response.Entity.ContentType = MediaType.TextPlain;
context.Response.Entity.ContentLength = first.Exception.Message.Length;
using (var sw = new StreamWriter(context.Response.Entity.Stream))
{
sw.Write(first.Exception.Message);
}
}
return PipelineContinuation.Continue;
}
}
There's some things to be aware of with the above:
If a handler were to throw a JsonReaderException, it would also be processed here.
It doesn't check what media types the client accepts. This is different from exceptions thrown by Handlers that do go through codec selection.
Tried setting context.OperationResult to context.ServerErrors - but it doesn't go through the codec.
Related
new to dropwizard! is there anyway that I can manually return different http status codes from the apis? basically something similar to this!
#GET
#Timed
public MyObject getMyObject(#QueryParam("id") Optional<String> id) {
MyObj myObj = myDao.getMyObject(id)
if (myObj == null) {
//return status.NOT_FOUND; // or something similar
// or more probably
// getResponseObjectFromSomewhere.setStatus(mystatus)
}
return myObj;
}
It's as simple as throwing a WebApplicationException.
#GET
#Timed
public MyObject getMyObject(#QueryParam("id") Optional<String> id) {
MyObject myObj = myDao.getMyObject(id)
if (myObj == null) {
throw new WebApplicationException(404);
}
return myObj;
}
As you get further along you may want to put together custom exceptions which you can read more about here.
I would recommend using the JAX-RS Response object instead of returning your actual domain object in the response. It serves as an excellent standard for including metadata with your response object and provides a nice builder for handling status codes, headers, customer content types, etc.
//import javax.ws.rs.core.Response above
#GET
#Timed
public Response getMyObject(#QueryParam("id") Optional<String> id) {
MyObject myObj = myDao.getMyObject(id)
if (myObj == null) {
//you can also provide messaging or other metadata if it is useful
return Response.status(Response.Status.NOT_FOUND).build()
}
return Response.ok(myObj).build();
}
The simplest way is to return an Optional<MyObject>. Dropwizard will automatically throw a 404 when your result is Optional.absent() or Optional.empty() if you use the dropwizard-java8 bundle.
Just do:
#GET
#Timed
public Optional<MyObject> getMyObject(#QueryParam("id") Optional<String> id) {
Optional<MyObject> myObjOptional = myDao.getMyObject(id)
return myObjOptional;
}
Obviously you need to update your DAO according by returning Optional.fromNullable(get(id)) for Guava or Optional.ofNullable(get(id)) for Java8 bundle.
There is no need to play around with custom Response objects unless you want to return a custom status code outside of 200 and 404
I am in the early stages of developing a new module.
Much of it is laid out in terms of the models etc. Also have the migrations all set up and my database now has the tables for my module.
I am encountering the following error when calling ContentManager.New<myPart> and would like some help please.
Error is this:
An unhandled exception has occurred and the request was terminated. Please refresh the page. If the error persists, go back
Specified cast is not valid.
System.InvalidCastException: Specified cast is not valid.
at Orchard.ContentManagement.ContentCreateExtensions.New[T]
(IContentManager manager, String contentType)
The chunk of code that fires the exception is this:
public static T New<T>(this IContentManager manager, string contentType) where T : class, IContent {
var contentItem = manager.New(contentType);
if (contentItem == null)
return null;
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
return part;
}
Here are the various parts to my module that are related to the operation i am struggling with:
ContentPart
public class GoogleMapsSettingsPart : ContentPart<GoogleMapsSettingsPartRecord>
{
public string ApiKey {
get { return Record.ApiKey; }
set { Record.ApiKey = value; }
}
}
ContentPartRecord
public class GoogleMapsSettingsPartRecord : ContentPartRecord
{
public virtual string ApiKey { get; set; }
}
Handler
public GoogleMapsSettingsPartHandler(IRepository<GoogleMapsSettingsPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
Migration for this table
// Settings Table
SchemaBuilder.CreateTable("GoogleMapsSettingsPartRecord", table => table
.ContentPartRecord()
.Column("ApiKey", DbType.String, c => c.WithLength(60))
);
Some of the code from the controller for this model etc
public AdminController(IContentManager contentManager, IShapeFactory shapeFactory, IServiceLocatorService serviceLocatorService, INotifier notifier)
{
_contentManager = contentManager;
_serviceLocatorService = serviceLocatorService;
_notifier = notifier;
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
/// <summary>
/// Display Settings
/// </summary>
/// <returns></returns>
public ActionResult Settings()
{
var settings = _serviceLocatorService.GoogleMapsSettings;
var editor = CreateSettingsEditor(settings);
var model = _services.ContentManager.BuildEditor(settings);
return View((object)model);
}
Finally - the Services where my call throws this exception
private GoogleMapsSettingsPart _settings;
public GoogleMapsSettingsPart GoogleMapsSettings
{
get {
if (_settings == null)
{
_settings = _contentManager.Query<GoogleMapsSettingsPart, GoogleMapsSettingsPartRecord>().List().FirstOrDefault();
if (_settings == null)
{
_settings = _contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
}
}
return _settings;
}
}
The actual line where the exception happens is _settings = _contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
I have tried all sorts of stuff in place of "GoogleMapsSettings" though nothing is working.
I'm pretty sure at this point it's something simple, though it's avoiding me..My limited knowledge of Orchard is stumping me
Any help would be appreciated :)
The exception is thrown because your content type does not have the part you specified to get.
_contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
This method call creates a new content item of type GoogleMapSettings and gets the content item as a GoogleMapsSettingsPart. However, it seems that GoogleMapSettings content type does not have a GoogleMapsSettingsPart. That's why the exception gets thrown here.
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
You must either attach the part dynamically to your content type or do it in a migration (or manually in the admin, but that's not a good idea). Your migration should look like this.
this.ContentDefinitionManager.AlterTypeDefinition("GoogleMapsSettings",
alt => alt
.WithPart("GoogleMapsSettingsPart");
Ok, so I fixed it...
My understanding of how Orchard works is still very much in the learning stages.
for this particular operation I didn't want to have a content type in the admin - though not sure why after adding the ContentType it still didn't work...
anyway, adding the lines below to my handler took care of the rest. I believe it's actually creating a temporary type so one isn't needed in the system.
public GoogleMapsSettingsPartHandler(IRepository<GoogleMapsSettingsPartRecord> repository)
{
Filters.Add(new ActivatingFilter<GoogleMapsSettingsPart>("GoogleMapsSettings"));
Filters.Add(StorageFilter.For(repository));
Filters.Add(new TemplateFilterForRecord<GoogleMapsSettingsPartRecord>("GoogleMapsSettings", "Parts/GoogleMapsSettings"));
}
I'v got the same error, but in my case it was everything ok with migration class.
The reason was unlucky merge, which deleted my driver class of my part.
Just look at this code of Activating method of ContentPartDriverCoordinator class. In my case there was no partInfo for my content part and resulted part became ContentPart, so casting throws an exception
var partInfos = _drivers.SelectMany(cpp => cpp.GetPartInfo()).ToList();
foreach (var typePartDefinition in contentTypeDefinition.Parts) {
var partName = typePartDefinition.PartDefinition.Name;
var partInfo = partInfos.FirstOrDefault(pi => pi.PartName == partName);
var part = partInfo != null
? partInfo.Factory(typePartDefinition)
: new ContentPart { TypePartDefinition = typePartDefinition };
context.Builder.Weld(part);
}
I find myself working a grails application that is being deployed as a fat jar built by a custom plugin that uses dropwizard to configure jetty.
It seems as though dropwizard doesn't allow facilitate the use of a plain old web.xml or jetty.xml and instead everything is set by java config at startup (i.e. using com.yammer.dropwizard.config.Environment).
Am I missing something here? Is there some way to map a 404 back to a URL or any kind of web page I can override so that a Jetty 404 isn't the default.
(yes I'm aware I could do something with the load balancer to redirect 404s)
I dont know how it is in grails, but this helps in java with dropwizard 0.7.1 run() method:
ResourceConfig jrConfig = environment.jersey().getResourceConfig();
environment.jersey().register(new RestErrorsHandler(jrConfig ));
Create this class for the mapping of exceptions -> give back an individual response!
#Provider
public class RestErrorsHandler implements ExceptionMapper<Exception> {
/**
* Deletes all ExpetionMappers.
*
* #param jrConfig
*/
public RestErrorsHandler(
ResourceConfig jrConfig)
{
// Remove all of Dropwizard's custom ExceptionMappers
Set<?> dwSingletons = jrConfig.getSingletons();
List<Object> singletonsToRemove = new ArrayList<Object>();
for (Object s : dwSingletons) {
// Remove all Exception mappers
if (s instanceof ExceptionMapper) {
singletonsToRemove.add(s);
}
}
for (Object s : singletonsToRemove) {
jrConfig.getSingletons().remove(s);
}
}
public Response toResponse(
Exception exception)
{
//Handle different exceptions in another way
if (exception.getClass().equals(JsonParseException.class)){
Response response = RestErrorsHandler.generalResponse(exception);
return response ;
} else if(exception.getClass().equals(JsonParseException.class)){
Response response = RestErrorsHandler.generalResponse(exception);
return response ;
} else if(exception.getClass().equals(Class.class)){
Response response = RestErrorsHandler.generalResponse(exception);
return response ;
}
//genral problem -> output default
Response response = RestErrorsHandler.generalResponse(exception);
return response ;
}
public static Response generalResponse(Exception exception)
{
return Response.status(Status.BAD_REQUEST).type(MediaType.TEXT_PLAIN)
.entity("if you just want to give back a string, but could also be default html page or whatever").build();
}
}
I am trying to find a solution to validate if XML data sent in a POST request are fulfilling a given custom XML schema.
If I use the XmlMediaTypeFormatter delivered with ASP.NET Web API I don't have a schema validation available, as far as I can see. For example: If I have a model type...
public class Order
{
public string Code { get; set; }
public int Quantity { get; set; }
}
...and a POST action in an ApiController...
public HttpResponseMessage Post(Order order)
{
if (ModelState.IsValid)
{
// process order...
// send 200 OK response for example
}
else
// send 400 BadRequest response with ModelState errors in response body
}
...I can post the following "wrong" XML data and will get a 200 OK response nevertheless:
User-Agent: Fiddler
Host: localhost:45678
Content-Type: application/xml; charset=utf-8
<Order> <Code>12345</Nonsense> </Order> // malformed XML
Or:
<Order> <CustomerName>12345</CustomerName> </Order> // invalid property
Or:
<Customer> <Code>12345</Code> </Customer> // invalid root
Or:
"Hello World" // no XML at all
etc., etc.
The only point where I have a validation of the request is model binding: In request example 1, 3 and 4 the order passed into the Post method is null, in example 2 the order.Code property is null which I could invalidate by testing for order == null or by marking the Code property with a [Required] attribute. I could send this validation result back in the response with a 400 "BadRequest" Http status code and validation messages in the response body. But I cannot tell exactly what was wrong and can't distinguish between the wrong XML in example 1, 3 and 4 (no order has been posted, that's the only thing I can see) - for instance.
Requiring that an Order has to be posted with a specific custom XML schema, for example xmlns="http://test.org/OrderSchema.xsd", I would like to validate if the posted XML is valid with respect to this schema and, if not, send schema validation errors back in the response. To achieve this I have started with a custom MediaTypeFormatter:
public class MyXmlMediaTypeFormatter : MediaTypeFormatter
{
// constructor, CanReadType, CanWriteType, ...
public override Task<object> ReadFromStreamAsync(Type type, Stream stream,
HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
var task = Task.Factory.StartNew(() =>
{
using (var streamReader = new StreamReader(stream))
{
XDocument document = XDocument.Load(streamReader);
// TODO: exceptions must the catched here,
// for example due to malformed XML
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(null, "OrderSchema.xsd");
var msgs = new List<string>();
document.Validate(schemaSet, (s, e) => msgs.Add(e.Message));
// msgs contains now the list of XML schema validation errors
// I want to send back in the response
if (msgs.Count == 0)
{
var order = ... // deserialize XML to order
return (object)order;
}
else
// WHAT NOW ?
}
});
return task;
}
}
This works so far as long as everything is correct.
But I don't know what to do if msgs.Count > 0. How can I "transfer" this validation result list to the Post action or how can I create a Http response that contains those XML schema validation messages?
Also I am unsure if a custom MediaTypeFormatter is the best extensibility point for such a XML schema validation and if my approach isn't the wrong way. Would possibly a custom HttpMessageHandler/DelegatingHandler be a better place for this? Or is there perhaps something much simpler out of the box?
If I were doing this I wouldn't use the Formatter. The primary goal of a formatter is to convert a wire representation to a CLR type. Here you have an XML document that you want to validate against a schema which is a different task altogether.
I would suggest creating a new MessageHandler to do the validation. Derive from DelegatingHandler and if the content type is application/xml load the content into XDocument and validate. If it fails, then throw a HttpResponseException.
Just add your MessageHandler to the Configuration.MessageHandlers collection and you are set.
The problem with using a derived XmlMediaTypeFormatter is that you are now executing at some point embedded inside the ObjectContent code and it is likely to be tricky to cleanly exit out. Also, making XmlMediaTypeFormatter any more complex is probably not a great idea.
I had a stab at creating the MessageHandler. I did not actually try running this code, so buyer beware. Also, the task stuff gets pretty hairy if you avoid blocking the caller. Maybe someone will clean that code up for me, anyway here it is.
public class SchemaValidationMessageHandler : DelegatingHandler {
private XmlSchemaSet _schemaSet;
public SchemaValidationMessageHandler() {
_schemaSet = new XmlSchemaSet();
_schemaSet.Add(null, "OrderSchema.xsd");
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
if (request.Content != null && request.Content.Headers.ContentType.MediaType == "application/xml")
{
var tcs = new TaskCompletionSource<HttpResponseMessage>();
var task = request.Content.LoadIntoBufferAsync() // I think this is needed so XmlMediaTypeFormatter will still have access to the content
.ContinueWith(t => {
request.Content.ReadAsStreamAsync()
.ContinueWith(t2 => {
var doc = XDocument.Load(t2.Result);
var msgs = new List<string>();
doc.Validate(_schemaSet, (s, e) => msgs.Add(e.Message));
if (msgs.Count > 0) {
var responseContent = new StringContent(String.Join(Environment.NewLine, msgs.ToArray()));
tcs.TrySetException(new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.BadRequest) {
Content = responseContent
}));
} else {
tcs.TrySetResult(base.SendAsync(request, cancellationToken).Result);
}
});
});
return tcs.Task;
} else {
return base.SendAsync(request, cancellationToken);
}
}
By trial and error I found a solution (for the WHAT NOW ? placeholder in the question's code):
//...
else
{
PostOrderErrors errors = new PostOrderErrors
{
XmlValidationErrors = msgs
};
HttpResponseMessage response = new HttpResponseMessage(
HttpStatusCode.BadRequest);
response.Content = new ObjectContent(typeof(PostOrderErrors), errors,
GlobalConfiguration.Configuration.Formatters.XmlFormatter);
throw new HttpResponseException(response);
}
...with the response class like this:
public class PostOrderErrors
{
public List<string> XmlValidationErrors { get; set; }
//...
}
That seems to work and the response looks like this then:
HTTP/1.1 400 Bad Request
Content-Type: application/xml; charset=utf-8
<PostOrderErrors xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<XmlValidationErrors>
<string>Some error text...</string>
<string>Another error text...</string>
</XmlValidationErrors>
</PostOrderErrors>
I have a need to access the encoded stream in OpenRasta before it gets sent to the client. I have tried using a PipelineContributor and registering it before KnownStages.IEnd, tried after KnownStages.IOperationExecution and after KnownStages.AfterResponseConding but in all instances the context.Response.Entity stream is null or empty.
Anyone know how I can do this?
Also I want to find out the requested codec fairly early on yet when I register after KnowStages.ICodecRequestSelection it returns null. I just get the feeling I am missing something about these pipeline contributors.
Without writing your own Codec (which, by the way, is really easy), I'm unaware of a way to get the actual stream of bytes sent to the browser. The way I'm doing this is serializing the ICommunicationContext.Response.Entity before the IResponseCoding known stage. Pseudo code:
class ResponseLogger : IPipelineContributor
{
public void Initialize(IPipeline pipelineRunner)
{
pipelineRunner
.Notify(LogResponse)
.Before<KnownStages.IResponseCoding>();
}
PipelineContinuation LogResponse(ICommunicationContext context)
{
string content = Serialize(context.Response.Entity);
}
string Serialize(IHttpEntity entity)
{
if ((entity == null) || (entity.Instance == null))
return String.Empty;
try
{
using (var writer = new StringWriter())
{
using (var xmlWriter = XmlWriter.Create(writer))
{
Type entityType = entity.Instance.GetType();
XmlSerializer serializer = new XmlSerializer(entityType);
serializer.Serialize(xmlWriter, entity.Instance);
}
return writer.ToString();
}
}
catch (Exception exception)
{
return exception.ToString();
}
}
}
This ResponseLogger is registered the usual way:
ResourceSpace.Uses.PipelineContributor<ResponseLogger>();
As mentioned, this doesn't necessarily give you the exact stream of bytes sent to the browser, but it is close enough for my needs, since the stream of bytes sent to the browser is basically just the same serialized entity.
By writing your own codec, you can with no more than 100 lines of code tap into the IMediaTypeWriter.WriteTo() method, which I would guess is the last line of defense before your bytes are transferred into the cloud. Within it, you basically just do something simple like this:
public void WriteTo(object entity, IHttpEntity response, string[] parameters)
{
using (var writer = XmlWriter.Create(response.Stream))
{
XmlSerializer serializer = new XmlSerializer(entity.GetType());
serializer.Serialize(writer, entity);
}
}
If you instead of writing directly to to the IHttpEntity.Stream write to a StringWriter and do ToString() on it, you'll have the serialized entity which you can log and do whatever you want with before writing it to the output stream.
While all of the above example code is based on XML serialization and deserialization, the same principle should apply no matter what format your application is using.