XML-RPC API best practices for exceptions

I am implementing a XML-RPC API for my Zope app.

The API will be used by a CRM.

Both systems have company entities, which are mapped by an ID.
There is a field mapping, and there are also restrictions on field lengths, e.g. street name must not be too long, other wise my Zope app cannot print address labels.

Assume, the CRM sends a request to the create_company endpoint, but e.g. the street name is too long.

As a first attempt on the side of my Zope app, I implemented a try-catch block, with the input validation in the try block, and a return Fault(faultCode="1", faultString="street name is too long") in the except block.

Works fine, the client gets a reasonable error message, but.. the transaction is NOT aborted on the side of my Zope app.

This means, if there is something with side effects in my try block, it will be persisted even when the request is borked.

try:
    validate()
    # maybe side effects
except ValidationError:
    return Fault(faultCode="1", faultString="street name is too long")
else:
    # update company
    return company.getId()

I then tried to re-raise an exception in the except block, which resulted in a generic 500 server fault for the client with no useful information.

try:
    validate()
    # maybe side effects
except ValidationError:
    raise ValueError  # or raise Fault
else:
    # update company
    return company.getId()

My last attempt was to issue an transaction.abort() in the exception block, which does not seem to have any effect. If there were side effects in the try block, they get persisted.

try:
    validate()
    # maybe side effects
except ValidationError:
    transaction.abort()
    return Fault(faultCode="1", faultString="street name is too long")
else:
    # update company
    return company.getId()

Anybody has a hint what to do?

REST is no option for now (due to pure Zope, time constraints... ).

Is the only chance to take 100% care that in the try-block no side effects occur?

Or what is the proper way to tell the client there is a problem with the data transmitted?

Separate input validation from side effects.
Note that with zope.schema, you can formulate e.g. length restrictions; it also provides tools to verify objects against a schema. dm.zope.schema.dataschema contains utilities to bridge between dicts and schema controlled objects.

You can abort the transaction in the except block -- and this abort will eliminate all previous side effects -- only following side effects will get committed when the request ends normally. However, I always discourage local transaction control.

I strongly favor separate input validation. However, you should also be able to customize exception handling at the end of request processing. When I remember right (--> look at the code to verify), then you can register exception views which decide about how to process the specific exception in the current request context.

transaction.abort() is what I would have go for as well. Strange enough stuff it still persists.

Thank you for your replies.

Thanks to the input of @icemac I decided to keep the return Fault way so the client gets a proper error message, but instead of using a transaction.abort, I went with a transaction.doom.

Now, this works as expected for my use case.

I wrote a short post about my learnings - I hope it is all correct what I say :smiley:

Plone Foundation Code of Conduct