Skip to content

Commit b11ad94

Browse files
committed
Merge pull request markfinger#64 from benmvp/master
Add ability to pass additional request headers to render server
2 parents 2a357f1 + acfe533 commit b11ad94

File tree

4 files changed

+68
-20
lines changed

4 files changed

+68
-20
lines changed

README.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ python-react provides an interface to a render server which is capable of render
5454
from your python process.
5555

5656
Render requests should provide a path to a JS file that exports a React component. If you want to pass
57-
data to the component, you can optionally provide a second argument that will be used as the component's
57+
data to the component, you can optionally provide a second argument that will be used as the component's
5858
`props` property.
5959

6060
```python
@@ -77,10 +77,10 @@ If the object is coerced to a string, it will emit the value of the `markup` att
7777

7878
Render servers are typically Node.js processes which sit alongside the python process and respond to network requests.
7979

80-
To add a render server to your project, you can refer to the [basic rendering example](examples/basic_rendering)
81-
for a simple server that will cover most cases. The key files for the render server are:
80+
To add a render server to your project, you can refer to the [basic rendering example](examples/basic_rendering)
81+
for a simple server that will cover most cases. The key files for the render server are:
8282
- [render_server.js](examples/basic_rendering/render_server.js) - the server's source code
83-
- [package.json](examples/basic_rendering/package.json) - the server's dependencies, installable with
83+
- [package.json](examples/basic_rendering/package.json) - the server's dependencies, installable with
8484
[npm](http://npmjs.com)
8585

8686

@@ -92,13 +92,13 @@ setup involves a build tool and a python package that can integrate it.
9292

9393
The two most popular build tools are:
9494

95-
- [Webpack](https://webpack.github.io) - compiles your files into browser-executable code and provides a
95+
- [Webpack](https://webpack.github.io) - compiles your files into browser-executable code and provides a
9696
variety of tools and processes which can simplify complicated workflows.
97-
- [Browserify](http://browserify.org/) - has a lot of cross-over with webpack. Is argurably the easiest of the
97+
- [Browserify](http://browserify.org/) - has a lot of cross-over with webpack. Is argurably the easiest of the
9898
two to use, but it tends to lag behind webpack in functionality.
9999

100-
For React projects, you'll find that webpack is the usual recommendation. Webpack's hot module replacement,
101-
code-splitting, and a wealth of loaders are the features typically cited as being irreplaceable.
100+
For React projects, you'll find that webpack is the usual recommendation. Webpack's hot module replacement,
101+
code-splitting, and a wealth of loaders are the features typically cited as being irreplaceable.
102102
[react-hot-loader](https://github.com/gaearon/react-hot-loader) is a particularly useful tool, as it allows
103103
changes to your components to be streamed live into your browser.
104104

@@ -121,8 +121,8 @@ javascript worlds.
121121
render_component
122122
----------------
123123

124-
Renders a component to its initial HTML. You can use this method to generate HTML on the server
125-
and send the markup down on the initial request for faster page loads and to allow search engines
124+
Renders a component to its initial HTML. You can use this method to generate HTML on the server
125+
and send the markup down on the initial request for faster page loads and to allow search engines
126126
to crawl your pages for SEO purposes.
127127

128128

@@ -147,6 +147,12 @@ render_component(
147147

148148
# An optional object which will be used instead of the default renderer
149149
renderer=None,
150+
151+
# An optional dictionary of request header information (such as `Accept-Language`)
152+
# to pass along with the request to the render server
153+
request_headers={
154+
'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'
155+
},
150156
)
151157
```
152158

@@ -156,25 +162,25 @@ via Django's static file finders.
156162
By default, render_component relies on access to a render server that exposes an endpoint compatible
157163
with [react-render's API](https://github.com/markfinger/react-render). If you want to use a different
158164
renderer, pass in an object as the `renderer` arg. The object should expose a `render` method which
159-
accepts the `path`, `data`, and `to_static_markup` arguments.
165+
accepts the `path`, `data`, `to_static_markup`, and `request_headers` arguments.
160166

161167

162168
Render server
163169
-------------
164170

165171
Earlier versions of this library would run the render server as a subprocess, this tended to make development
166-
easier, but introduced instabilities and opaque behaviour. To avoid these issues python-react now relies on
172+
easier, but introduced instabilities and opaque behaviour. To avoid these issues python-react now relies on
167173
externally managed process. While managing extra processes can add more overhead initially, it avoids pain down
168174
the track.
169175

170176
If you only want to run the render server in particular environments, change the `RENDER` setting to
171-
False. When `RENDER` is False, the render server is not used directly, but it's wrapper will return similar
177+
False. When `RENDER` is False, the render server is not used directly, but it's wrapper will return similar
172178
objects with the `markup` attribute as an empty string.
173179

174180

175181
### Usage in development
176182

177-
In development environments, it can be easiest to set the `RENDER` setting to False. This ensures that the
183+
In development environments, it can be easiest to set the `RENDER` setting to False. This ensures that the
178184
render server will not be used, hence you only need to manage your python process.
179185

180186
Be aware that the render servers provided in the examples and elsewhere rely on Node.js's module system

react/render.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .render_server import render_server
55

66

7-
def render_component(path, props=None, to_static_markup=False, renderer=render_server):
7+
def render_component(path, props=None, to_static_markup=False, renderer=render_server, request_headers=None):
88
if not os.path.isabs(path):
99
abs_path = staticfiles.find(path)
1010
if not abs_path:
@@ -14,4 +14,4 @@ def render_component(path, props=None, to_static_markup=False, renderer=render_s
1414
if not os.path.exists(path):
1515
raise ComponentSourceFileNotFound(path)
1616

17-
return renderer.render(path, props, to_static_markup)
17+
return renderer.render(path, props, to_static_markup, request_headers)

react/render_server.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ def __unicode__(self):
2020

2121

2222
class RenderServer(object):
23-
def render(self, path, props=None, to_static_markup=False):
23+
def render(self, path, props=None, to_static_markup=False, request_headers=None):
2424
url = conf.settings.RENDER_URL
25-
25+
2626
if props is not None:
2727
serialized_props = json.dumps(props, cls=JSONEncoder)
2828
else:
@@ -39,11 +39,17 @@ def render(self, path, props=None, to_static_markup=False):
3939
serialized_options = json.dumps(options)
4040
options_hash = hashlib.sha1(serialized_options.encode('utf-8')).hexdigest()
4141

42+
all_request_headers = {'content-type': 'application/json'}
43+
44+
# Add additional requests headers if the requet_headers dictionary is specified
45+
if request_headers is not None:
46+
all_request_headers.update(request_headers)
47+
4248
try:
4349
res = requests.post(
4450
url,
4551
data=serialized_options,
46-
headers={'content-type': 'application/json'},
52+
headers=all_request_headers,
4753
params={'hash': options_hash}
4854
)
4955
except requests.ConnectionError:

tests/test_rendering.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
from optional_django import six
33
import mock
44
from react.conf import Conf
5+
from react import render_server
56
from react.render import render_component
67
from react.render_server import RenderedComponent
78
from react.exceptions import ReactRenderingError, ComponentSourceFileNotFound
89
from .settings import Components
10+
import json
911

1012

1113
class TestRendering(unittest.TestCase):
@@ -102,4 +104,38 @@ def test_render_setting_is_respected(self):
102104
self.assertIsInstance(rendered, RenderedComponent)
103105
self.assertEqual(rendered.markup, '')
104106
self.assertEqual(str(rendered), '')
105-
self.assertEqual(rendered.props, '{"name": "world!"}')
107+
self.assertEqual(rendered.props, '{"name": "world!"}')
108+
109+
@mock.patch('requests.post')
110+
def test_can_pass_additional_request_headers(self, requests_post_mock):
111+
mock_json = {
112+
'markup': '<div>Hello</div>',
113+
}
114+
mock_url = 'http://localhost/render'
115+
116+
response_mock = mock.Mock()
117+
response_mock.status_code = 200
118+
response_mock.text = json.dumps(mock_json)
119+
response_mock.json = mock.Mock(return_value=mock_json)
120+
requests_post_mock.return_value = response_mock
121+
122+
mock_settings = Conf()
123+
mock_settings.configure(RENDER_URL=mock_url)
124+
with mock.patch('react.conf.settings', mock_settings):
125+
component = render_component(
126+
path=Components.HELLO_WORLD_JSX,
127+
props={'name': 'world!'},
128+
request_headers={
129+
'Accept-language': 'fr-FR,en-US,en',
130+
},
131+
)
132+
133+
requests_post_mock.assert_called_with(
134+
mock_url,
135+
data=mock.ANY,
136+
params=mock.ANY,
137+
headers={
138+
'content-type': 'application/json',
139+
'Accept-language': 'fr-FR,en-US,en',
140+
},
141+
)

0 commit comments

Comments
 (0)