自定義消息解析器, AbstractHttpMessageConverter

概要:

通常在做WEB項目開發, 有安全級別比較高的項目, 參數一般會加密, 或者添加簽名, 但在插入數據庫之前會做一些校驗,  每個方法都添加校驗代碼,顯得有些冗餘, 這些都可以通過自定義解析器實現,

1, 代碼實現:

sprng.xml 中添加 默認的json解析, 和自定義消息解析

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<property name="messageConverters">
			<list>
				<ref bean="mappingJacksonHttpMessageConverter"/>
				<bean class="com.study.test.expand.MyHttpMessageConverter" />
			</list>
		</property>
	</bean>

    <bean id="mappingJacksonHttpMessageConverter"
          class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/html;charset=UTF-8</value>
                <value>text/plain;charset=UTF-8</value>
                <value>application/json;charset=UTF-8</value>
            </list>
        </property>
    </bean>

自定義解析器 Java代碼:

public class MyHttpMessageConverter extends AbstractHttpMessageConverter<User> {

	public MyHttpMessageConverter() {
    //註冊對應的contentType
		super(new MediaType("application", "type-t", Charset.forName("UTF-8")));
	}
	@Override
	protected boolean supports(Class<?> clazz) {
		// 只處理返回值是User對象的方法
		return User.class.isAssignableFrom(clazz);
	}
	@Override
	protected User readInternal(Class<? extends User> clazz, HttpInputMessage inputMessage) throws IOException,
			HttpMessageNotReadableException {
		String json = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));
		if (StringUtils.isEmpty(json)) {
			return null;
		}
		String[] split = json.split(",");
		String name = split[0].split("#")[1];
		String hobby = split[1].split("#")[1];
		User user = new User();
		user.setName(name);
		user.setHobby(hobby);
		return user;
	}
	@Override
	protected void writeInternal(User user, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
		String msg = "自定義類型application/type-t, 名稱:" +user.getName() +", 愛好: " + user.getHobby();
		outputMessage.getBody().write(msg.getBytes("UTF-8"));
	}
}
// 實體類
public class User {
	private String name;
	private String hobby;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getHobby() {
		return hobby;
	}
	public void setHobby(String hobby) {
		this.hobby = hobby;
	}
}
// controller
@Controller
@RequestMapping("/home")
public class HomeController {
	@RequestMapping(value = "index3")
	@ResponseBody
	public User index3(@RequestBody User user) {
		return user;
	}
}

PostMan 解析, Headers 裏面添加頭信息 Content-Type :  application/type-t;charset=UTF-8 

2, 源碼解析

2.1, 參數解析,

查看 RequestResponseBodyMethodProcessor 的 readWithMessageConverters 方法,  循環所有解析器,

首先判斷是否是GenericHttpMessageConverter 的子類,如果不是, 轉換爲HttpMessageConverter 接口對象,並調用read方法,

該方法由子類 AbstractHttpMessageConverter 實現, 並最終調用readInternal 方法, 而 readInternal 方法由我們自定義解析器重寫,

2.2 方法返回

查看 RequestResponseBodyMethodProcessor的 writeWithMessageConverters 方法,   首先會解析出所有符合的contentType, 循環所有contentType, 選擇符合的頭信息

循環所有的消息解析器,  判斷是否是 GenericHttpMessageConverter的子類, 如果不是直接調用 canWrite 方法, 判斷是否可以寫,

最終調用write方法, 由子類AbstractHttpMessageConverter 實現, 並調用 writeInternal方法, 這個方法恰好被我們的自定義解析器重寫

3, 問題總結

1, spring.xml中自定義的消息解析,  mappingJacksonHttpMessageConverter 在前面, 而canWrite爲true,進入循環,執行完後,會return, 並沒有執行我們的MyHttpMessageConverter自定義消息器

  解答: 因爲在write的過程中, 處理完返回值,直接寫入了response中, 所以不會寫第二遍,  可以在RequestMapping 註解中, 使用produces 返回指定的類型,   consumes 接受指定的類型

@RequestMapping(value = "index3", produces = { "application/type-t;charset=UTF-8" })

2,  writeInternal 自定義消息器, 往 response 寫數據. 出現中文亂碼

    解答, 因爲消息解析器在寫的過程中,默認使用的是ISO8859-1,  而項目中用的是UTF-8 編碼格式, 所以返回內容轉換一下就行   

 

 

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