分析MockHttpServletRequestBuilder中content和param的區別

最近在做一個自動測試的平臺,使用的是Spring的自帶測試庫。如何使用,這裏不再說了,網上有很多,推薦開濤寫的博客

我的主要測試代碼:

//主要構造mock請求類,可以不用看
try {
            for(TestClass tmpClass:classes){
                List<TestUrl> urls = tmpClass.getUrls();
                for(TestUrl url:urls){
                    String urlString = url.getCompositeUrl();

                    MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(urlString);
                    initRequestParams(requestBuilder,url);
                    mockMvc.perform(requestBuilder).andDo(MockMvcResultHandlers.print());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

//初始化request參數
private void initRequestParams(MockHttpServletRequestBuilder builder,TestUrl url) {
        //設置編碼格式
        builder.characterEncoding("utf-8");
        TestParameter parameter = url.getParamter();
        TestContent content = url.getContent();
        TestSession sessionData = url.getSession();

        //添加session
        if(sessionData != null){
            MockHttpSession mockHttpSession = new MockHttpSession();
            Map<String, Object> maps = sessionData.getSession();
            Set<Map.Entry<String, Object>> sets = maps.entrySet();

            for(Map.Entry<String, Object> entry:sets){
                mockHttpSession.setAttribute(entry.getKey(), entry.getValue());
            }
            builder.session(mockHttpSession);
        }
        if(parameter != null){
            Map<String, String> paramMaps = parameter.getParams();
            Set<Map.Entry<String, String>> sets = paramMaps.entrySet();

            StringBuilder paramBuilder = new StringBuilder();
            for(Map.Entry<String, String> entry:sets){
                paramBuilder.append('&');
                paramBuilder.append(entry.getKey());
                paramBuilder.append('=');
                paramBuilder.append(entry.getValue());
            }
            String bodyData = paramBuilder.deleteCharAt(0).toString();
            System.out.println(bodyData);
            builder.content(bodyData);//這裏是重點
            builder.contentType(MediaType.APPLICATION_FORM_URLENCODED);//這裏是重點

//          for(Map.Entry<String, String> entry:sets){
//              builder.param(entry.getKey(), entry.getValue());
//          }
            return;
        }
    }

根據HTTP協議,當body中參數以form表單格式進行提交時,在服務器端通過request.getParameter(“name”)來獲取是可以獲取到的,效果和在Url中使用參數是一樣的。然而當我在這裏設置參數時發現是有問題的,test結果顯示缺少參數,而缺少的參數是在body裏面的,這說明mock並沒有content-type類型去解析content的數據。

於是我查看了Spring中MockHttpServletRequestBuilder的源碼

...
private String contentType;//content類型
private byte[] content;//content數據
private final MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();//參數列表

....

    /**
     * Set the request body as a UTF-8 String.
     * @param content the body content
     */
    public MockHttpServletRequestBuilder content(String content) {
        try {
            this.content = content.getBytes("UTF-8");
        }
        catch (UnsupportedEncodingException e) {
            // should never happen
        }
        return this;
    }

/**
     * Add a request parameter to the {@link MockHttpServletRequest}.
     * <p>If called more than once, new values get added to existing ones.
     * @param name the parameter name
     * @param values one or more values
     */
    public MockHttpServletRequestBuilder param(String name, String... values) {
        addToMultiValueMap(this.parameters, name, values);//將name和value添加到parameter中,允許一個名字對應多個value值
        return this;
    }


Ok,以上是初始化的方法。下面是重點,builder的最終目的是build一個MockHttpServletRequest,而這個工作是有buildRequest()方法來完成的。

部分:
try {
            if (this.url.getRawQuery() != null) {
                request.setQueryString(this.url.getRawQuery());
            }

            MultiValueMap<String, String> queryParams =
                    UriComponentsBuilder.fromUri(this.url).build().getQueryParams();

            for (Entry<String, List<String>> entry : queryParams.entrySet()) {
                for (String value : entry.getValue()) {
                    value = (value != null) ? UriUtils.decode(value, "UTF-8") : null;
                    request.addParameter(UriUtils.decode(entry.getKey(), "UTF-8"), value);
                }
            }
        }
        catch (UnsupportedEncodingException ex) {
            // shouldn't happen
        }

        for (String name : this.parameters.keySet()) {
            for (String value : this.parameters.get(name)) {
                request.addParameter(name, value);
            }
        }

        request.setContentType(this.contentType);
        request.setContent(this.content);
        request.setCharacterEncoding(this.characterEncoding);

由buildRequest的代碼可以發現,Mock將URL的參數和通過使用param添加的參數添加到request中的parameter中了,而將content內容、類型並沒有進行解析,直接添加到request的content中了。

讓我們再查看MockHttpServletRequest的代碼:

@Override
    public String getParameter(String name) {
        String[] arr = (name != null ? this.parameters.get(name) : null);
        return (arr != null && arr.length > 0 ? arr[0] : null);
    }

MockHttpServletRequest實現了HttpServletRequest,而在getParameter進行了重寫,即從parameter的map中進行讀取。

這也就解釋了文章開頭提出的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章