Werkzeug: ์Šฌ๋ž˜์‹œ ์ถ”๊ฐ€๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

์— ๋งŒ๋“  2018๋…„ 01์›” 31์ผ  ยท  7์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: pallets/werkzeug

Werkzeug์™€ Flask๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ REST API๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋งฅ๋ฝ์—์„œ ์šฐ๋ฆฌ๋Š” ํ›„ํ–‰ ์Šฌ๋ž˜์‹œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ž๋™ ๋™์ž‘์„ ์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” GET์™€ ๋‹ค๋ฅธ ์š”์ฒญ ๊ฐ„์˜ ๋น„๋Œ€์นญ์œผ๋กœ ์ด์–ด์ง€๋ฉฐ ์—ฌ๊ธฐ์—์„œ๋Š” ์—„๊ฒฉํ•˜๊ฒŒ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€์— ์—ฐ๊ฒฐ๋˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๊ทœ์น™ ํด๋ž˜์Šค( Rule.match ๋ฐ RequestSlash )๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ 1์ฐจ ๊ตฌ์„ฑ ์˜ต์…˜์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? URL ๋งต์—์„œ append_slash (๋™์ผํ•œ Django ์˜ต์…˜๊ณผ ๋ณ‘ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด)์™€ ๊ฐ™์€ ๊ฒƒ?

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

app.url_map.strict_slashes = False ๋Š” ์ด๊ฒƒ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์ „๋ถ€์ž…๋‹ˆ๋‹ค.

๋ชจ๋“  7 ๋Œ“๊ธ€

app.url_map.strict_slashes = False ๋Š” ์ด๊ฒƒ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์ „๋ถ€์ž…๋‹ˆ๋‹ค.

์Šฌ๋ž˜์‹œ๊ฐ€ ์—†์œผ๋ฉด ์‹ค์ œ๋กœ ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ 404๋กœ ์›ํ•œ๋‹ค.

ํ˜„์žฌ ๋™์ž‘์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

| ๊ทœ์น™์— ์Šฌ๋ž˜์‹œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. | ๊ฒฝ๋กœ์— ์Šฌ๋ž˜์‹œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค | strict_slashes | ๊ฒฐ๊ณผ |
|-|-|-|-|
| ์—” | ์—” | True | (๊ฒฝ๊ธฐ) |
| ์—” | ์—” | False | (๊ฒฝ๊ธฐ) |
| ์—” | ์™€ | True | 404 |
| ์—” | ์™€ | False | 404 |
| ์™€ | ์—” | True | 301 |
| ์™€ | ์—” | False | (๊ฒฝ๊ธฐ) |
| ์™€ | ์™€ | True | (๊ฒฝ๊ธฐ) |
| ์™€ | ์™€ | False | (๊ฒฝ๊ธฐ) |

๊ทœ์น™์— ํ›„ํ–‰ ์Šฌ๋ž˜์‹œ๊ฐ€ ์žˆ์ง€๋งŒ ๊ฒฝ๋กœ์—๋Š” ์—†๋Š” ๊ฒฝ์šฐ 404๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ๋‹ค์Œ์„ ํ†ตํ•ด ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

class StrictRule(Rule):
    def match(self, path, method=None):
        try:
            result = super(StrictRule, self).match(path, method)
        except RequestSlash:
            return None

        return result

ํ•˜์ง€๋งŒ ๋‚ด๋ถ€์— ์†์„ ๋Œ€์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ง€์ • ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด Flask์—์„œ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋ชจ๋“  ์‚ฌ๋žŒ์„ ์œ„ํ•œ ๊ทœ์น™์„ ๋งŒ๋“ค๋ ค๊ณ  ํ•˜์ง€ ์•Š๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ‘œ์ค€์œผ๋กœ ์ œ๊ณต๋˜๋Š” ๊ฒƒ ์ด์ƒ์˜ ๊ทœ์น™์„ ์›ํ•˜๋ฉด ์ด๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Flash์˜ ๊ฒฝ์šฐ url_rule_class ๋ฅผ ์ƒˆ ํด๋ž˜์Šค๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Flask ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”. http://flask.pocoo.org/docs/1.0/api/?highlight=rule#flask.Flask.url_rule_class

@englander

https://github.com/pallets/werkzeug/issues/1246#issuecomment -362099342์— ๋”ฐ๋ฅด๋ฉด ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๊ทœ์น™ ๊ตฌํ˜„์— ์žˆ์Šต๋‹ˆ๋‹ค. RequestSlash ๋Š” https://github.com/pallets/werkzeug/blob/a220671d66755a94630a212378754bb432811158/src/werkzeug/routing.py#L259 -L260์— ๋”ฐ๋ผ ๋‚ด๋ถ€ ์˜ˆ์™ธ๋กœ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œ์‹œ๋˜์–ด ์žˆ์–ด ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ฝ”๋“œ.

๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋ณธ ๊ทœ์น™์ด ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์ง€์›ํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค๋Š” ๋ฐ์—๋Š” ํ™•์‹คํžˆ ๋™์˜ํ•˜์ง€๋งŒ ํ›„ํ–‰ ์Šฌ๋ž˜์‹œ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์€ ๋งค์šฐ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค. ์„ ํ–‰ ๊ธฐ์ˆ ์˜ ๊ฒฝ์šฐ ์ด ํŒจํ„ด์€ APPEND_SLASH : https://docs.djangoproject.com/en/dev/ref/settings/#append -slash๋ฅผ ํ†ตํ•ด Django ๊ตฌ์„ฑ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ๋…ธ์ถœ๋˜๋Š” ๋ช‡ ์•ˆ ๋˜๋Š” ํŒจํ„ด ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ "๋‚ด๋ถ€"๋Š” "๋‚ด๋ถ€ ์ „์šฉ"์ด ์•„๋‹ˆ๋ผ "๋‚ด๋ถ€์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋จ"์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋ณด์—ฌ์ฃผ์‹  ์ฝ”๋“œ๋Š” ์‚ฌ์šฉํ•ด๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

@davidism ์„ค๋ช… ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์ด๋Œ€๋กœ ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰

๊ด€๋ จ ๋ฌธ์ œ

ngaya-ll picture ngaya-ll  ยท  8์ฝ”๋ฉ˜ํŠธ

golf-player picture golf-player  ยท  10์ฝ”๋ฉ˜ํŠธ

SimonSapin picture SimonSapin  ยท  12์ฝ”๋ฉ˜ํŠธ

alexgurrola picture alexgurrola  ยท  5์ฝ”๋ฉ˜ํŠธ

davidism picture davidism  ยท  9์ฝ”๋ฉ˜ํŠธ