base a Yesod web on a url with a non empty path - yesod

If I append a base path to the approot property in settings.yml like
approot="http://localhost:3000/base"
calling this URL in the browser shows "Not Found" as message, instead of giving properly the Home page.
I could not find any way to solve it.
...
I see, I have to Rewrite the URL without the base prefix before submitting it to the yesod task, and the approot is only used to be prepended to all page links.
Is there a proper way to handle theese non-empty path based URL's ?

There are two relatively simple approaches to this:
Create a middleware which will strip the leading "base" value from the pathInfo.
Override cleanPath (in the Yesod typeclass) to do the same.
In general, an approot with a leading path piece is most useful where there's some front server which is calling out to your Yesod app (either via reverse proxy or FastCGI).

Following M.Snoyman hints, here is a cleanPath version that solves the question, just for the approot case of the ApprootMaster constructor, as generated by yesod init , as an addition to the generated module Foundation.hs:
{-# LANGUAGE PackageImports #-}
import qualified Data.Text as Txt
import qualified "url" Network.URL as Url
import qualified Network.HTTP.Types as H
import qualified Data.Text.Encoding as TE
import qualified Data.List as L
import qualified "utf8-string" Data.ByteString.UTF8 as UTF8BS
-- instance Yesod App where
-- cleanPath - A function used to clean up path segments.
-- It returns Right with a clean path or Left with a new set of pieces the user should be redirected to
-- cleanPath :: Yesod a => a -> [Text] -> Either [Text] [Text]
cleanPath master s =
if corrected == s'
then Right $ cutoffBasePrefix s'
else Left $ cutoffBasePrefix corrected -- to be redirected
where
-- avoid redirection on slash ended urls by junking the last piece if it's null
s' = if not (L.null s) && Txt.null (last s) then init s else s
corrected = filter (not . Txt.null) s'
-- strToUtf8BS = TE.encodeUtf8 . Txt.pack -- packs to UTF16 then encodes to UTF8
strToUtf8BS = UTF8BS.fromString
-- cut off "base prefix" or leave as it is
cutoffBasePrefix segmts =
case approot of
ApprootMaster f ->
case Url.importURL $ Txt.unpack $ f master of
Nothing -> segmts -- not expected, incorrect url in settings.yml approot
Just url -> let basePrefixSegmts = H.decodePathSegments $ strToUtf8BS $ Url.url_path url in
case basePrefixSegmts of
[] -> segmts
_ -> if basePrefixSegmts `L.isPrefixOf` segmts
then drop (length basePrefixSegmts) segmts
else segmts
_ -> segmts
with theese additional package dependencies:
, url >= 2.1.2
, network
, http-types
, utf8-string

Related

Parse &-character in GET query using Django and Vue

We are using axios to pass GET request to our django instance, that splits it into search terms and runs the search. This has been working fine until we ran into an edge case. We use urlencode to ensure that strings do not have empty spaces or others
So generalize issue, we have TextField called "name" and we want to search for term "A & B Company". However, issue is that when the request reaches django.
What we expected was that name=A%20&%20B%20Company&field=value would be parsed as name='A & B Company' and field='value'.
Instead, it is parsed as name='A ' 'B Company' and field='value'. The & symbol is incorrectly treated as separator, despite being encoded.
Is there a way to indicate django GET parameter that certain & symbols are part of the value, instead of separators for fields?
You can use the lib urllib
class ModelExample(models.Model):
name = models.TextField()
# in view...
from urllib.parse import parse_qs
instance = ModelExample(name="name=A%20&%20B%20Company&field=value")
dict_qs = parse_qs(instance.name)
dict_qs contains a dict with decoded querystring
You can find more informations about urllib.parse here: https://docs.python.org/3/library/urllib.parse.html

set a parameter with default value to route url in django

Hy all!
I'm new to python / django and I came across a problem that I can not solve. I have a route configured for the site's home (1) and a route configured for categories (2):
1) url(r'^$', IndexView().home, name='home')
2) url(r'^categoria/(?P<path>.*)/$', IndexView().by_category, name='by_category')
I need to set my home url to open a category by default, something like www.mysite.com/c=defaul_category
I tried in some ways, including: url (r '^ / (? P \ w +) / $', IndexView (). Home, name = 'home'). But I know it's incorrect.
So... I have no idea how to do this. Could someone help me?
Thank you
You should tell django that path in by_category url may be omitted. You have at least two options here:
1 - create one more url without path but with passed path variable as 3-rd argument in url:
url(r'^/(?P<c=vinhos>\w+)/$', IndexView().home, name='home')
url(r'^categoria/(?P<path>.*)/$', IndexView().by_category, name='by_category')
url(r'^categoria/$', IndexView().by_category,
{'path': 'default_path'}, name='default_category')
2 - change regex pattern to make it possible to omit path parameter. Here | (or sign) added in the end of path group:
url(r'^categoria/(?P<path>.*|)/$', IndexView().by_category, name='by_category')
More about omitting url parameters Django optional url parameters

How do I sort Flask/Werkzeug routes in the order in which they will be matched?

I have a function that I'm using to list routes for my Flask site and would like to be sure it is sorting them in the order in which they will be matched by Flask/Werkzeug.
Currently I have
def routes(verbose, wide, nostatic):
"""List routes supported by the application"""
for rule in sorted(app.url_map.iter_rules()):
if nostatic and rule.endpoint == 'static':
continue
if verbose:
fmt = "{:45s} {:30s} {:30s}" if wide else "{:35s} {:25s} {:25s}"
line = fmt.format(rule, rule.endpoint, ','.join(rule.methods))
else:
fmt = "{:45s}" if wide else "{:35s}"
line = fmt.format(rule)
print(line)
but this just seems to sort the routes in the order I've defined them in my code.
How do I sort Flask/Werkzeug routes in the order in which they will be matched?
Another approach: Specifying a host and a path and seeing what rule applies.
urls = app.url_map.bind(host)
try:
m = urls.match(match, "GET")
z = '{}({})'.format(m[0], ','.join(["{}='{}'".format(arg, m[1][arg]) for arg in m[1]] +
["host='{}'".format(host)]))
return z
except NotFound:
return
On each request Flask/Werkzeug reordered self.map.update() rules if it need with route weights (see my another answer URL routing conflicts for static files in Flask dev server).
You can found that Map.update method update _rules (see https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/routing.py#L1313).
So you can do the same thing:
sorted(current_app.url_map._rules, key=lambda x: x.match_compare_key())
Or if all routes already loaded and any request done (called Map.build or Map.match) just use app.url_map._rules - it will be sorted. In request app.url_map._rules already sorted, because Map.match was called before dispatcher (match rule with dispatcher).
You also can found that Map.update called in Map.iter_rules, so you can just use this method current_app.url_map.iter_rules() (see https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/routing.py#L1161).

list of 2ld.1ld (2nd level domain.1st(top)level domain) names?

I am looking for a list of (doesnt matter if its not all, just needs to be big as its for generating dummy data)
Im looking for a list like
.net.nz
.co.nz
.edu.nz
.govt.nz
.com.au
.govt.au
.com
.net
any ideas where I can locate a list?
There are answers here. Most of them are relating to the use of http://publicsuffix.org/, and even some implementations to use it were given in some languages, like Ruby.
To get all the ICANN domains, this python code should work for you:
import requests
url = 'https://publicsuffix.org/list/public_suffix_list.dat'
page = requests.get(url)
icann_domains = []
for line in page.text.splitlines():
if 'END ICANN DOMAINS' in line:
break
elif line.startswith('//'):
continue
else:
domain = line.strip()
if domain:
icann_domains.append(domain)
print(len(icann_domains)) # 7334 as of Nov 2018
Remove the break statement to get private domains as well.
Be careful as you will get some domains like this: *.kh (e.g. http://www.mptc.gov.kh/dns_registration.htm). The * is a wildcard.

Change subdomain + language with django-localeurl

I am using django-localeurl to change the language of a project based on a suffix after the domain (example.com/en , example.com/hu etc). However I also have subdomains for the countries which are exactly the same as the suffixes.
How can I modify the locale-url or add another filter to the links so that I could change the suffix and subdomain at the same time?
f.e.
example.com -> hu.example.com/hu -> es.example.com/es etc.
Here there is the localeurl chlocale function:
def chlocale(url, locale):
"""
Changes the URL's locale prefix if the path is not locale-independent.
Otherwise removes locale prefix.
"""
_, path = utils.strip_script_prefix(url)
_, path = utils.strip_path(path)
return utils.locale_url(path, locale)
chlocale = stringfilter(chlocale)
register.filter('chlocale', chlocale)
That's my call as URL href:
Hungary
This one actually returns only the relative path not the http full address of the webpage, so it's OK to attach prefix http://sitename.domain at the beginning before the {{ request.path... }} call.
domain = Site.objects.get_current().domain
Hungary
A little hacky but perhaps what you are looking for.