I have created a filter api with more than one arguements to pass in url to filter product in my iOS application. The api is written in Python in Django rest framework. The api is working fine in postman, but when I check the code for Swift in postman is shows some complex url structure and I am confused how to apply that url string my swift code.
My serializer class:
class ProductOptionsSerializer(serializers.ModelSerializer):
optionproductid = ProductSerializer()
optionattributeid = AttributeSerializer()
optionsid = serializers.IntegerField()
class Meta:
model = Productoptions
fields = ('optionproductid', 'optionattributeid', 'optionsid')
View:
class GetProductByMulipleFilter(ListAPIView):
serializer_class = ProductOptionsSerializer
def get_queryset(self):
query_params = self.request.query_params
somethings = query_params.get('filters', None)
# create an empty list for parameters to be filters by
somethingParams = []
serializer = self.serializer_class()
# create the list based on the query parameters
if somethings is not None:
for something in somethings.split(','):
somethingParams.append(str(something))
if somethings is not None:
queryset_list = Productoptions.objects.all()
queryset_list = queryset_list.filter(optionattributeid__attributename__in=somethingParams)
return queryset_list
url
url(r'^$', GetProductByMulipleFilter.as_view(), name='filters')
http://192.168.1.13:8000/api/?filters=puma,nike
Its working fine and showing right results. But the code in swift is showing url like:
let request = NSMutableURLRequest(url: NSURL(string: "http://192.168.1.13:8000/api/?filters=puma%2Cnike")! as URL
My question is why the url in Swift code is not showing parameter seperated with ',' only.
Because request encode , symbol into %2C while it was processed. You can find more about URL percent encoding on Wiki
When a character from the reserved set (a "reserved character") has
special meaning (a "reserved purpose") in a certain context, and a URI
scheme says that it is necessary to use that character for some other
purpose, then the character must be percent-encoded. Percent-encoding
a reserved character involves converting the character to its
corresponding byte value in ASCII and then representing that value as
a pair of hexadecimal digits. The digits, preceded by a percent sign
(%) which is used as an escape character, are then used in the URI in
place of the reserved character. (For a non-ASCII character, it is
typically converted to its byte sequence in UTF-8, and then each byte
value is represented as above.)
You can try
let param = "anyThing"
let request = NSMutableURLRequest(url: NSURL(string: "http://192.168.1.13:8000/api/?filters=\(param)")! as URL
Related
I'm working at the moment on an implementation of webauthn on a project. The main point is to give the possibility to user to use FaceId or fingerprint scan on their mobile on the website.
I tried the djoser version of webauthn but I wanted to give the possibility to user that already have an account so I took the implementation of webauthn of djoser and I updated it to make it working with already created account.
I can ask for the signup request of a webauthn token and create the webauthn token with the front (Angular) where I use #simplewebauthn/browser ("#simplewebauthn/browser": "^6.3.0-alpha.1") . Everything is working fine there.
I use the latest version of djoser by pulling git and the version of webauthn is 0.4.7 linked to djoser.
djoser #git+https://github.com/sunscrapers/djoser.git#abdf622f95dfa2c6278c4bd6d50dfe69559d90c0
webauthn==0.4.7
But when I send back to the backend the result of the registration, I have an error:
Authentication rejected. Error: Invalid signature received..
Here's the SignUpView:
permission_classes = (AllowAny,)
def post(self, request, ukey):
co = get_object_or_404(CredentialOptions, ukey=ukey)
webauthn_registration_response = WebAuthnRegistrationResponse(
rp_id=settings.DJOSER["WEBAUTHN"]["RP_ID"],
origin=settings.DJOSER["WEBAUTHN"]["ORIGIN"],
registration_response=request.data,
challenge=co.challenge,
none_attestation_permitted=True,
)
try:
webauthn_credential = webauthn_registration_response.verify()
except RegistrationRejectedException as e:
return Response(
{api_settings.NON_FIELD_ERRORS_KEY: format(e)},
status=status.HTTP_400_BAD_REQUEST,
)
user = User.objects.get(username=request.data["username"])
user_serializer = CustomUserSerializer(user)
co.challenge = ""
co.user = user
co.sign_count = webauthn_credential.sign_count
co.credential_id = webauthn_credential.credential_id.decode()
co.public_key = webauthn_credential.public_key.decode()
co.save()
return Response(user_serializer.data, status=status.HTTP_201_CREATED)
And I based my work on https://github.com/sunscrapers/djoser/blob/abdf622f95dfa2c6278c4bd6d50dfe69559d90c0/djoser/webauthn/views.py#L53
Here's also the SignUpRequesrtView where I edited some little things to make it working the way I want:
class SignupRequestView(APIView):
permission_classes = (AllowAny,)
def post(self, request):
CredentialOptions.objects.filter(username=request.data["username"]).delete()
serializer = WebauthnSignupSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
co = serializer.save()
credential_registration_dict = WebAuthnMakeCredentialOptions(
challenge=co.challenge,
rp_name=settings.DJOSER["WEBAUTHN"]["RP_NAME"],
rp_id=settings.DJOSER["WEBAUTHN"]["RP_ID"],
user_id=co.ukey,
username=co.username,
display_name=co.display_name,
icon_url="",
)
return Response(credential_registration_dict.registration_dict)
And I also updated the WebAuthnSignupSerializer to retrieve an check if there's an account with the username given and if yes, create the CredentialOptions:
class WebauthnSignupSerializer(serializers.ModelSerializer):
class Meta:
model = CredentialOptions
fields = ("username", "display_name")
def create(self, validated_data):
validated_data.update(
{
"challenge": create_challenge(
length=settings.DJOSER["WEBAUTHN"]["CHALLENGE_LENGTH"]
),
"ukey": create_ukey(length=settings.DJOSER["WEBAUTHN"]["UKEY_LENGTH"]),
}
)
return super().create(validated_data)
def validate_username(self, username):
if User.objects.filter(username=username).exists():
return username
else:
raise serializers.ValidationError(f"User {username} does not exist.")```
EDIT: TL;DR;
#simplewebauthn/browser encodes the signature as base64url while duo-labs/py_webauthn expects a hex encoded signature.
Well, it's not really an answer, but rather a little "assistance".
You can check whether the signature is valid using this little tool (at the bottom of the page): https://webauthn.passwordless.id/demos/playground.html
At least, using that, you will know if your data is correct or if something was stored wrongly. There are so many conversions from bytes to base64url and back that it's not always easy to track. Perhaps it is a data format/convertion issue? Like not converting to bytes, or accidentally double encoding as base64url.
Lastly, the stored public key has a different format depending on the algorithm. Either "raw" or "ASN.1" wrapped, in case you have a problem with the key itself.
Good luck!
EDIT:
While delving a bit into the source code of sunscrapers/djoser, I noticed something quite odd. While all data is encoded as base64, it appears the signature is hex encoded instead, see their test app
That seems to be because it uses duo-labs/py_webauthn as dependency which expects a hex encoded signature. On the other hand, the #simplewebauthn/browser lib encodes it into base64url, like all other data.
The verify() method expects a RegistrationResponse object as an argument, but you're passing it the entire request data. You need to extract the registrationResponse field from the request data and pass that to the verify() method instead.
Change this:
webauthn_registration_response = WebAuthnRegistrationResponse(
rp_id=settings.DJOSER["WEBAUTHN"]["RP_ID"],
origin=settings.DJOSER["WEBAUTHN"]["ORIGIN"],
registration_response=request.data, # <-- update this line
challenge=co.challenge,
none_attestation_permitted=True,)
To this:
webauthn_registration_response = WebAuthnRegistrationResponse(
rp_id=settings.DJOSER["WEBAUTHN"]["RP_ID"],
origin=settings.DJOSER["WEBAUTHN"]["ORIGIN"],
registration_response=request.data['registrationResponse'], # <-- update this line
challenge=co.challenge,
none_attestation_permitted=True,)
I'm sending some data to Django Rest Framework backend with Content-Type set as application/json.
If I try to send any JSON that is shaped like a typical nested key-value object, everything works fine.
But if I want to send a simple string like test, I get a 400 Bad request due to malformed JSON : JSON parse error - Expecting value: line 1 column 1 (char 0).
But aren't strings valid JSON values according to RFC 8259 ?
I don't understand from the official DRF documentation if this is a normal feature for Django JSON Parser or if I'm doing something wrong somewhere ?
The class-based view that I'm using is ListCreateAPIView:
class ObjectList(ListCreateAPIView):
"""
View dedicated to GET a list of instances or POST a new one
"""
serializer_class = MySerializer
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
# Filter objects based on user identity (token based)
queryset = MyObject.objects.filter(user=self.request.user)
return queryset
def post(self, request, format=None):
transect_data = request.data # Returns 400 Bad request
The piece of code that send data to the server (JS using Axios)
…
return axios
.post(API_URL + 'create/',
"test",
{
headers: headers,
}
)
.then(response => {
return response.data;
})
.catch((error) => {
console.log(error);
});
You can pass any JSON value [json.org], so that includes a string, but a string is put between double quotes ("…") and some characters should be escaped, so you send "test", not test.
test itself is not a valid JSON value. Indeed, you can check for example with the Online JSON valiator [jsonlint.com] if a JSON blob is valid JSON.
A string literal is, just like for example in Python and JavaScript thus put in an "envelope" of double quotes, and escapes certain characters such that if the string itself contains a double quote, that character is escaped properly.
I am trying to make a query system for my website, i think the best way and the most compact would be to assign search variable using url pattern.
So for example, i want to search objects of model User:
User sends HttpRequest to following url:
https://127.0.0.1/search/q="admin"
Now HttpRequest is also sent to search view, we somehow get q variable data.
def search(request):
for query in User.objects.all():
if q in query: # < We somehow need to get data of 'q'.
return HttpResponse(q)
Since i have admin in User.objects.all(), this should return HttpResponse of 'admin'.
How can this url pattern be made? So i can assign q variable from the url and then send it to system to find it?
I have problems with this URL:
https://127.0.0.1/search/q="admin"
There is no ? in the URL, so there is no query string, it's all part of the "path". Using characters like = and " in there will confuse a lot of things, if it works at all.
Either just do
https://127.0.0.1/search/admin
With an URL pattern like r'^search/(?P<querystring>.+)$', or
https://127.0.0.1/search/?q=admin
In this case the query string will be in request.GET['q']; it's also possible to use Django forms to process query parameters (e.g. for validating them).
You can capture named strings from URLs like this:
urls.py:
urlpatterns = [
url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]
views.py:
def page(request, num="1"):
There seems to be a bug in the way DRF renders hyper-linked URL's in the Self describing API. DRF is translating my url signature for S3 (django-storage) from %2B to '+' when being displayed. However format=json does not have this translation issue.
For example:
This is the Serializer:
class CatalogueSerializer(serializers.HyperlinkedModelSerializer):
image = HyperlinkedImageField()
class Meta:
model = CatalogueItem
fields = ('url', 'name', 'image')
HyperlinkedImageField:
class HyperlinkedImageField(serializers.ImageField):
def to_native(self, value):
request = self.context.get('request', None)
if value:
url = request.build_absolute_uri(value.url)
else:
url = 'null'
return url
Value of the URL is correct and the signature is right. However when DRF renders the URL it changes the Signature from this....
Good
Signature=lMG4NLl51IHeXWCU%2B2GPBN1vU30%3D&Expires=1404604768
to this:
Bad
Signature=lMG4NLl51IHeXWCU+2GPBN1vU30=&Expires=1404604768
Where the only difference is the translation of %2B to '+'.
I have tried to get around the following in my serializer:
def transform_image(self, obj, value):
return urllib.quote(value, safe="%/:=&?~#+!$,;'#()*[]")
But, no matter, it always does the translation of %2B to '+'.
Is there a work around?
You could wrap whatever string you want in "slugify": https://docs.djangoproject.com/en/dev/ref/utils/#django.utils.text.slugify
That makes sure that the string is always "URL safe"
I'm just getting started with Python, and I'm stuck on the syntax that I need to convert a set of request.POST parameters to Solr's query syntax.
The use case is a form defined like this:
class SearchForm(forms.Form):
text = forms.CharField()
metadata = forms.CharField()
figures = forms.CharField()
Upon submission, the form needs to generate a url-encoded string to pass to a URL client that looks like this:
text:myKeyword1+AND+metadata:myKeyword2+AND+figures:myKeyword3
Seems like there should be a simple way to do this. The closest I can get is
for f, v in request.POST.iteritems():
if v:
qstring += u'%s\u003A%s+and+' % (f,v)
However in that case the colon (\u003A) generates a Solr error because it is not getting encoded properly.
What is the clean way to do this?
Thanks!
I would recommend creating a function in the form to handle this rather than in the view. So after you call form.isValid() to ensure the data is acceptable. You can invoke a form.generateSolrQuery() (or whatever name you would like).
class SearchForm(forms.Form):
text = forms.CharField()
metadata = forms.CharField()
figures = forms.CharField()
def generateSolrQuery(self):
return "+and+".join(["%s\u003A%s" % (f,v) for f,v in self.cleaned_data.items()])
Isn't u'\u003A' simply the same as u':', which is escaped in URLs as %3A ?
qstring = '+AND+'.join(
[ u'%s%%3A%s'%(f,v) for f,v in request.POST.iteritems() if f]
)
Use django-haystack. It takes care of all the interfacing with Solr - both indexing and querying.