Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

94
Visualizações
Django conditional view processing decorator adds stale Etag

I have been trying to use the Django conditional view processing feature. Basically I want to deny update operations on an entity if it has since been modified by another user, and that seems to work well with the @condition decorator provided by Django.

However there is one issue which I noticed while testing it and later I checked in the Django sources and I found what I think could be a bug, but just wanted to confirm here first before submitting a bug report to Django and a fix.

The decorator is called when a new request comes in, it first calculates the ETag and Last Modified timestamp based on the functions passed in to the decorator, then it passes control over to the get_conditional_response() function. Here the ETag and Last Modified verification would be performed and if they don't match to what's provided in the request, the request will be denied. So far so good.

If the checks pass, the request is allowed and the view is called to process the request and generate the response. While processing the request, if it was an unsafe method e.g. PUT or PATCH, it would update the entity, which would most likely change the ETag and Last Modified values.

However, I noticed that a successful response to PUT or PATCH is sent back with the ETag or Last Modified timestamp calculated before the update was actually performed, and by now these values are invalid or stale. This to me seems wrong. Doing a fresh GET on the same entity then provides the user with updated ETag and Last Modified values in the response.

Don't you think, the condition() decorator should check if the request method is unsafe, then it should do a fresh calculation of ETag and Last Modified after the view processing, and then add the fresh values to the response?

about 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

I agree that there's a bug here, though I think it's a bit different from what you describe.

Conditional Requests are defined in RFC 7232, but unfortunately that document is not very explicit about exactly when the conditional headers should be used in a response. It does say:

2.4. When to Use Entity-Tags and Last-Modified Dates

In 200 (OK) responses to GET or HEAD, an origin server...

That might lead one to assume that use of the headers is not defined in other responses.

However, RFC 7231 explicitly allows for the use of ETags in the response to a PUT, matching the new representation (as was your intuition). However, note this caveat:

An origin server MUST NOT send a validator header field (Section 7.2), such as an ETag or Last-Modified field, in a successful response to PUT unless the request's representation data was saved without any transformation applied to the body...

That is, the client will use the presence or absence of the ETag to determine whether or not its representation (that it just sent as the body to PUT) was the one actually stored. (See this question for more detail on this point.)

However, Django's conditional request API does not allow for making this distinction. Specifically, there's no way for the user to indicate whether or not a view saved the representation without "transformation applied to the body". So there's no way for the condition() decorator to know whether or not adding an ETag is warranted.

So the only thing to do is to be conservative and not return conditional headers at all in this case. Feel free to create a ticket (or else I can do it).

about 4 years ago · Santiago Trujillo Relatório

0

Create a custom middleware to handle etag in the GET/HEAD request. The following code(Django 1.10) shows how to create and process etag using middleware.

Note: Don't enable USE_ETAGS in the settings file

from django.utils.cache import get_conditional_response, set_response_etag
from django.utils.http import unquote_etag


class ETag(object):
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # before view

        response = self.get_response(request)

        # after view
        try:
            if request.method in ('GET', 'HEAD'):
                if not response.has_header('ETag'):
                    set_response_etag(response)
                etag = response.get('ETag')
                return get_conditional_response(
                    request,
                    etag=unquote_etag(etag),
                    last_modified=None,
                    response=response,
                )
        except Exception, e:
            pass

        return response

i am using Django 1.10. If you are using lower versions then override process_response(self, request, response) method with the logic implemented inside the __call__ method. And don't forget to add this to the MIDDLEWARE/MIDDLEWARE_CLASSES in the settings file

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',


    # myapp contains middleware.py file and 
    # ETag class is implemented inside the middleware.py file 
    'myapp.middleware.Etag',
]
about 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda